Add IchimokuKumoTrend indicator support across application
- Introduced IchimokuKumoTrend indicator in GeneticService with configuration settings for tenkanPeriods, kijunPeriods, senkouBPeriods, offsetPeriods, senkouOffset, and chikouOffset. - Updated ScenarioHelpers to handle creation and validation of the new indicator type. - Enhanced CustomScenario, backtest, and scenario pages to include IchimokuKumoTrend in indicator lists and parameter mappings. - Modified API and types to reflect the addition of the new indicator in relevant enums and mappings.
This commit is contained in:
@@ -108,6 +108,13 @@ public class GeneticService : IGeneticService
|
|||||||
{
|
{
|
||||||
["period"] = 20.0,
|
["period"] = 20.0,
|
||||||
["multiplier"] = 2.0
|
["multiplier"] = 2.0
|
||||||
|
},
|
||||||
|
[IndicatorType.IchimokuKumoTrend] = new()
|
||||||
|
{
|
||||||
|
["tenkanPeriods"] = 9.0,
|
||||||
|
["kijunPeriods"] = 26.0,
|
||||||
|
["senkouBPeriods"] = 52.0,
|
||||||
|
["offsetPeriods"] = 26.0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -196,6 +203,13 @@ public class GeneticService : IGeneticService
|
|||||||
{
|
{
|
||||||
["period"] = (5.0, 50.0),
|
["period"] = (5.0, 50.0),
|
||||||
["multiplier"] = (1.0, 5.0)
|
["multiplier"] = (1.0, 5.0)
|
||||||
|
},
|
||||||
|
[IndicatorType.IchimokuKumoTrend] = new()
|
||||||
|
{
|
||||||
|
["tenkanPeriods"] = (5.0, 20.0),
|
||||||
|
["kijunPeriods"] = (10.0, 40.0),
|
||||||
|
["senkouBPeriods"] = (20.0, 80.0),
|
||||||
|
["offsetPeriods"] = (10.0, 50.0)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -217,7 +231,8 @@ public class GeneticService : IGeneticService
|
|||||||
[IndicatorType.StochasticCross] = ["stochPeriods", "signalPeriods", "smoothPeriods", "kFactor", "dFactor"],
|
[IndicatorType.StochasticCross] = ["stochPeriods", "signalPeriods", "smoothPeriods", "kFactor", "dFactor"],
|
||||||
[IndicatorType.Stc] = ["cyclePeriods", "fastPeriods", "slowPeriods"],
|
[IndicatorType.Stc] = ["cyclePeriods", "fastPeriods", "slowPeriods"],
|
||||||
[IndicatorType.LaggingStc] = ["cyclePeriods", "fastPeriods", "slowPeriods"],
|
[IndicatorType.LaggingStc] = ["cyclePeriods", "fastPeriods", "slowPeriods"],
|
||||||
[IndicatorType.BollingerBandsPercentBMomentumBreakout] = ["period", "multiplier"]
|
[IndicatorType.BollingerBandsPercentBMomentumBreakout] = ["period", "multiplier"],
|
||||||
|
[IndicatorType.IchimokuKumoTrend] = ["tenkanPeriods", "kijunPeriods", "senkouBPeriods", "offsetPeriods"]
|
||||||
};
|
};
|
||||||
|
|
||||||
public GeneticService(
|
public GeneticService(
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ public static class Enums
|
|||||||
LaggingStc,
|
LaggingStc,
|
||||||
SuperTrendCrossEma,
|
SuperTrendCrossEma,
|
||||||
DualEmaCross,
|
DualEmaCross,
|
||||||
BollingerBandsPercentBMomentumBreakout
|
BollingerBandsPercentBMomentumBreakout,
|
||||||
|
IchimokuKumoTrend
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SignalType
|
public enum SignalType
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||||
<PackageReference Include="FluentAssertions" Version="8.8.0" />
|
<PackageReference Include="FluentAssertions" Version="8.8.0" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
||||||
<PackageReference Include="xunit" Version="2.5.3" />
|
<PackageReference Include="xunit" Version="2.5.3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||||
|
|||||||
@@ -17,4 +17,5 @@ public class IndicatorsResultBase
|
|||||||
public List<StdDevResult> StdDev { get; set; }
|
public List<StdDevResult> StdDev { get; set; }
|
||||||
public List<SuperTrendResult> SuperTrend { get; set; }
|
public List<SuperTrendResult> SuperTrend { get; set; }
|
||||||
public List<ChandelierResult> ChandelierLong { get; set; }
|
public List<ChandelierResult> ChandelierLong { get; set; }
|
||||||
|
public List<IchimokuResult> Ichimoku { get; set; }
|
||||||
}
|
}
|
||||||
@@ -43,6 +43,14 @@ namespace Managing.Domain.Strategies
|
|||||||
|
|
||||||
public double? DFactor { get; set; }
|
public double? DFactor { get; set; }
|
||||||
|
|
||||||
|
// Ichimoku-specific parameters
|
||||||
|
public int? TenkanPeriods { get; set; }
|
||||||
|
public int? KijunPeriods { get; set; }
|
||||||
|
public int? SenkouBPeriods { get; set; }
|
||||||
|
public int? OffsetPeriods { get; set; }
|
||||||
|
public int? SenkouOffset { get; set; }
|
||||||
|
public int? ChikouOffset { get; set; }
|
||||||
|
|
||||||
public User User { get; set; }
|
public User User { get; set; }
|
||||||
|
|
||||||
public virtual List<LightSignal> Run(HashSet<Candle> candles)
|
public virtual List<LightSignal> Run(HashSet<Candle> candles)
|
||||||
|
|||||||
@@ -46,6 +46,14 @@ public class LightIndicator
|
|||||||
|
|
||||||
[Id(13)] public double? DFactor { get; set; }
|
[Id(13)] public double? DFactor { get; set; }
|
||||||
|
|
||||||
|
// Ichimoku-specific parameters
|
||||||
|
[Id(14)] public int? TenkanPeriods { get; set; }
|
||||||
|
[Id(15)] public int? KijunPeriods { get; set; }
|
||||||
|
[Id(16)] public int? SenkouBPeriods { get; set; }
|
||||||
|
[Id(17)] public int? OffsetPeriods { get; set; }
|
||||||
|
[Id(18)] public int? SenkouOffset { get; set; }
|
||||||
|
[Id(19)] public int? ChikouOffset { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a LightIndicator back to a full Indicator
|
/// Converts a LightIndicator back to a full Indicator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -70,7 +78,13 @@ public class LightIndicator
|
|||||||
StochPeriods = StochPeriods,
|
StochPeriods = StochPeriods,
|
||||||
CyclePeriods = CyclePeriods,
|
CyclePeriods = CyclePeriods,
|
||||||
KFactor = KFactor,
|
KFactor = KFactor,
|
||||||
DFactor = DFactor
|
DFactor = DFactor,
|
||||||
|
TenkanPeriods = TenkanPeriods,
|
||||||
|
KijunPeriods = KijunPeriods,
|
||||||
|
SenkouBPeriods = SenkouBPeriods,
|
||||||
|
OffsetPeriods = OffsetPeriods,
|
||||||
|
SenkouOffset = SenkouOffset,
|
||||||
|
ChikouOffset = ChikouOffset
|
||||||
};
|
};
|
||||||
|
|
||||||
return baseIndicator;
|
return baseIndicator;
|
||||||
|
|||||||
291
src/Managing.Domain/Indicators/Trends/IchimokuKumoTrend.cs
Normal file
291
src/Managing.Domain/Indicators/Trends/IchimokuKumoTrend.cs
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
using Managing.Core;
|
||||||
|
using Managing.Domain.Candles;
|
||||||
|
using Managing.Domain.Indicators;
|
||||||
|
using Managing.Domain.Shared.Rules;
|
||||||
|
using Managing.Domain.Strategies.Base;
|
||||||
|
using Skender.Stock.Indicators;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Domain.Strategies.Trends;
|
||||||
|
|
||||||
|
public class IchimokuKumoTrend : IndicatorBase
|
||||||
|
{
|
||||||
|
public List<LightSignal> Signals { get; set; }
|
||||||
|
|
||||||
|
public IchimokuKumoTrend(
|
||||||
|
string name,
|
||||||
|
int tenkanPeriods = 9,
|
||||||
|
int kijunPeriods = 26,
|
||||||
|
int senkouBPeriods = 52,
|
||||||
|
int offsetPeriods = 26,
|
||||||
|
int? senkouOffset = null,
|
||||||
|
int? chikouOffset = null) : base(name, IndicatorType.IchimokuKumoTrend)
|
||||||
|
{
|
||||||
|
Signals = new List<LightSignal>();
|
||||||
|
TenkanPeriods = tenkanPeriods; // Tenkan-sen periods
|
||||||
|
KijunPeriods = kijunPeriods; // Kijun-sen periods
|
||||||
|
SenkouBPeriods = senkouBPeriods; // Senkou Span B periods
|
||||||
|
OffsetPeriods = offsetPeriods; // Default offset periods
|
||||||
|
SenkouOffset = senkouOffset; // Separate offset for Senkou span
|
||||||
|
ChikouOffset = chikouOffset; // Separate offset for Chikou span
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<LightSignal> Run(HashSet<Candle> candles)
|
||||||
|
{
|
||||||
|
// Need at least the greater of tenkanPeriods, kijunPeriods, senkouBPeriods, and all offset periods
|
||||||
|
var maxOffset = Math.Max(Math.Max(OffsetPeriods.Value, SenkouOffset ?? OffsetPeriods.Value), ChikouOffset ?? OffsetPeriods.Value);
|
||||||
|
var minRequired = Math.Max(Math.Max(Math.Max(TenkanPeriods.Value, KijunPeriods.Value), SenkouBPeriods.Value), maxOffset);
|
||||||
|
if (candles.Count <= minRequired)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ichimokuResults = CalculateIchimoku(candles.ToList());
|
||||||
|
if (ichimokuResults.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
ProcessKumoTrendSignals(ichimokuResults, candles);
|
||||||
|
|
||||||
|
return Signals.ToList();
|
||||||
|
}
|
||||||
|
catch (RuleException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<LightSignal> Run(HashSet<Candle> candles, IndicatorsResultBase preCalculatedValues)
|
||||||
|
{
|
||||||
|
// Need at least the greater of tenkanPeriods, kijunPeriods, senkouBPeriods, and all offset periods
|
||||||
|
var maxOffset = Math.Max(Math.Max(OffsetPeriods.Value, SenkouOffset ?? OffsetPeriods.Value), ChikouOffset ?? OffsetPeriods.Value);
|
||||||
|
var minRequired = Math.Max(Math.Max(Math.Max(TenkanPeriods.Value, KijunPeriods.Value), SenkouBPeriods.Value), maxOffset);
|
||||||
|
if (candles.Count <= minRequired)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<IchimokuResult> ichimokuResults = null;
|
||||||
|
|
||||||
|
// Use pre-calculated Ichimoku values if available
|
||||||
|
if (preCalculatedValues?.Ichimoku != null && preCalculatedValues.Ichimoku.Any())
|
||||||
|
{
|
||||||
|
// Filter pre-calculated Ichimoku values to match the candles we're processing
|
||||||
|
ichimokuResults = preCalculatedValues.Ichimoku
|
||||||
|
.Where(i => i.SenkouSpanA.HasValue && i.SenkouSpanB.HasValue && candles.Any(c => c.Date == i.Date))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no pre-calculated values or they don't match, fall back to regular calculation
|
||||||
|
if (ichimokuResults == null || !ichimokuResults.Any())
|
||||||
|
{
|
||||||
|
return Run(candles);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessKumoTrendSignalsFromResults(ichimokuResults, candles);
|
||||||
|
|
||||||
|
return Signals.ToList();
|
||||||
|
}
|
||||||
|
catch (RuleException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<CandleIchimoku> CalculateIchimoku(List<Candle> candles)
|
||||||
|
{
|
||||||
|
// Use Skender.Stock.Indicators GetIchimoku method with all supported parameters
|
||||||
|
IEnumerable<IchimokuResult> ichimokuResults;
|
||||||
|
|
||||||
|
// Use the appropriate overload based on which parameters are specified
|
||||||
|
if (SenkouOffset.HasValue && ChikouOffset.HasValue)
|
||||||
|
{
|
||||||
|
// Use separate offsets for Senkou and Chikou spans
|
||||||
|
ichimokuResults = candles.GetIchimoku(
|
||||||
|
tenkanPeriods: TenkanPeriods.Value,
|
||||||
|
kijunPeriods: KijunPeriods.Value,
|
||||||
|
senkouBPeriods: SenkouBPeriods.Value,
|
||||||
|
senkouOffset: SenkouOffset.Value,
|
||||||
|
chikouOffset: ChikouOffset.Value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use default offsetPeriods for both Senkou and Chikou spans
|
||||||
|
ichimokuResults = candles.GetIchimoku(
|
||||||
|
tenkanPeriods: TenkanPeriods.Value,
|
||||||
|
kijunPeriods: KijunPeriods.Value,
|
||||||
|
senkouBPeriods: SenkouBPeriods.Value,
|
||||||
|
offsetPeriods: OffsetPeriods.Value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ichimokuList = ichimokuResults.ToList();
|
||||||
|
|
||||||
|
var candleIchimokuResults = new List<CandleIchimoku>();
|
||||||
|
|
||||||
|
// Map IchimokuResult to CandleIchimoku
|
||||||
|
foreach (var ichimoku in ichimokuList)
|
||||||
|
{
|
||||||
|
// Find the corresponding candle
|
||||||
|
var candle = candles.FirstOrDefault(c => c.Date == ichimoku.Date);
|
||||||
|
if (candle == null) continue;
|
||||||
|
|
||||||
|
candleIchimokuResults.Add(new CandleIchimoku()
|
||||||
|
{
|
||||||
|
Close = candle.Close,
|
||||||
|
Open = candle.Open,
|
||||||
|
Date = candle.Date,
|
||||||
|
Ticker = candle.Ticker,
|
||||||
|
Exchange = candle.Exchange,
|
||||||
|
TenkanSen = ichimoku.TenkanSen ?? 0,
|
||||||
|
KijunSen = ichimoku.KijunSen ?? 0,
|
||||||
|
SenkouSpanA = ichimoku.SenkouSpanA ?? 0,
|
||||||
|
SenkouSpanB = ichimoku.SenkouSpanB ?? 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return candleIchimokuResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessKumoTrendSignals(List<CandleIchimoku> ichimokuResults, HashSet<Candle> candles)
|
||||||
|
{
|
||||||
|
var mappedData = ichimokuResults;
|
||||||
|
|
||||||
|
if (mappedData.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var previousCandle = mappedData[0];
|
||||||
|
foreach (var currentCandle in mappedData.Skip(1))
|
||||||
|
{
|
||||||
|
// For trend assessment, check if price is above or below the cloud
|
||||||
|
// The cloud is formed by Senkou Span A and Senkou Span B
|
||||||
|
var cloudTop = Math.Max(currentCandle.SenkouSpanA, currentCandle.SenkouSpanB);
|
||||||
|
var cloudBottom = Math.Min(currentCandle.SenkouSpanA, currentCandle.SenkouSpanB);
|
||||||
|
|
||||||
|
if (currentCandle.Close > cloudTop)
|
||||||
|
{
|
||||||
|
AddSignal(currentCandle, TradeDirection.Long, Confidence.None);
|
||||||
|
}
|
||||||
|
else if (currentCandle.Close < cloudBottom)
|
||||||
|
{
|
||||||
|
AddSignal(currentCandle, TradeDirection.Short, Confidence.None);
|
||||||
|
}
|
||||||
|
// If price is within the cloud, no signal (neutral)
|
||||||
|
|
||||||
|
previousCandle = currentCandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessKumoTrendSignalsFromResults(List<IchimokuResult> ichimokuResults, HashSet<Candle> candles)
|
||||||
|
{
|
||||||
|
if (ichimokuResults.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var previousResult = ichimokuResults[0];
|
||||||
|
foreach (var currentResult in ichimokuResults.Skip(1))
|
||||||
|
{
|
||||||
|
// Find the corresponding candle
|
||||||
|
var candle = candles.FirstOrDefault(c => c.Date == currentResult.Date);
|
||||||
|
if (candle == null || !currentResult.SenkouSpanA.HasValue || !currentResult.SenkouSpanB.HasValue)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// For trend assessment, check if price is above or below the cloud
|
||||||
|
// The cloud is formed by Senkou Span A and Senkou Span B
|
||||||
|
var cloudTop = Math.Max(currentResult.SenkouSpanA.Value, currentResult.SenkouSpanB.Value);
|
||||||
|
var cloudBottom = Math.Min(currentResult.SenkouSpanA.Value, currentResult.SenkouSpanB.Value);
|
||||||
|
|
||||||
|
if (candle.Close > cloudTop)
|
||||||
|
{
|
||||||
|
AddSignal(candle, TradeDirection.Long, Confidence.None);
|
||||||
|
}
|
||||||
|
else if (candle.Close < cloudBottom)
|
||||||
|
{
|
||||||
|
AddSignal(candle, TradeDirection.Short, Confidence.None);
|
||||||
|
}
|
||||||
|
// If price is within the cloud, no signal (neutral)
|
||||||
|
|
||||||
|
previousResult = currentResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IndicatorsResultBase GetIndicatorValues(HashSet<Candle> candles)
|
||||||
|
{
|
||||||
|
IEnumerable<IchimokuResult> ichimokuResults;
|
||||||
|
|
||||||
|
// Use the appropriate overload based on which parameters are specified
|
||||||
|
if (SenkouOffset.HasValue && ChikouOffset.HasValue)
|
||||||
|
{
|
||||||
|
// Use separate offsets for Senkou and Chikou spans
|
||||||
|
ichimokuResults = candles.GetIchimoku(
|
||||||
|
tenkanPeriods: TenkanPeriods.Value,
|
||||||
|
kijunPeriods: KijunPeriods.Value,
|
||||||
|
senkouBPeriods: SenkouBPeriods.Value,
|
||||||
|
senkouOffset: SenkouOffset.Value,
|
||||||
|
chikouOffset: ChikouOffset.Value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use default offsetPeriods for both Senkou and Chikou spans
|
||||||
|
ichimokuResults = candles.GetIchimoku(
|
||||||
|
tenkanPeriods: TenkanPeriods.Value,
|
||||||
|
kijunPeriods: KijunPeriods.Value,
|
||||||
|
senkouBPeriods: SenkouBPeriods.Value,
|
||||||
|
offsetPeriods: OffsetPeriods.Value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new IndicatorsResultBase()
|
||||||
|
{
|
||||||
|
Ichimoku = ichimokuResults.ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddSignal(CandleIchimoku candleSignal, TradeDirection direction, Confidence confidence)
|
||||||
|
{
|
||||||
|
var signal = new LightSignal(
|
||||||
|
candleSignal.Ticker,
|
||||||
|
direction,
|
||||||
|
confidence,
|
||||||
|
candleSignal,
|
||||||
|
candleSignal.Date,
|
||||||
|
candleSignal.Exchange,
|
||||||
|
Type,
|
||||||
|
SignalType,
|
||||||
|
Name);
|
||||||
|
if (!Signals.Any(s => s.Identifier == signal.Identifier))
|
||||||
|
{
|
||||||
|
Signals.AddItem(signal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddSignal(Candle candleSignal, TradeDirection direction, Confidence confidence)
|
||||||
|
{
|
||||||
|
var signal = new LightSignal(
|
||||||
|
candleSignal.Ticker,
|
||||||
|
direction,
|
||||||
|
confidence,
|
||||||
|
candleSignal,
|
||||||
|
candleSignal.Date,
|
||||||
|
candleSignal.Exchange,
|
||||||
|
Type,
|
||||||
|
SignalType,
|
||||||
|
Name);
|
||||||
|
if (!Signals.Any(s => s.Identifier == signal.Identifier))
|
||||||
|
{
|
||||||
|
Signals.AddItem(signal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CandleIchimoku : Candle
|
||||||
|
{
|
||||||
|
public decimal TenkanSen { get; set; }
|
||||||
|
public decimal KijunSen { get; set; }
|
||||||
|
public decimal SenkouSpanA { get; set; }
|
||||||
|
public decimal SenkouSpanB { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,6 +102,13 @@ public static class ScenarioHelpers
|
|||||||
indicator.Period.Value, indicator.Multiplier.Value),
|
indicator.Period.Value, indicator.Multiplier.Value),
|
||||||
IndicatorType.BollingerBandsPercentBMomentumBreakout => new BollingerBandsPercentBMomentumBreakout(indicator.Name,
|
IndicatorType.BollingerBandsPercentBMomentumBreakout => new BollingerBandsPercentBMomentumBreakout(indicator.Name,
|
||||||
indicator.Period.Value, indicator.Multiplier.Value),
|
indicator.Period.Value, indicator.Multiplier.Value),
|
||||||
|
IndicatorType.IchimokuKumoTrend => new IchimokuKumoTrend(indicator.Name,
|
||||||
|
indicator.TenkanPeriods ?? 9,
|
||||||
|
indicator.KijunPeriods ?? 26,
|
||||||
|
indicator.SenkouBPeriods ?? 52,
|
||||||
|
indicator.OffsetPeriods ?? 26,
|
||||||
|
indicator.SenkouOffset,
|
||||||
|
indicator.ChikouOffset),
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -292,6 +299,7 @@ public static class ScenarioHelpers
|
|||||||
IndicatorType.LaggingStc => SignalType.Signal,
|
IndicatorType.LaggingStc => SignalType.Signal,
|
||||||
IndicatorType.SuperTrendCrossEma => SignalType.Signal,
|
IndicatorType.SuperTrendCrossEma => SignalType.Signal,
|
||||||
IndicatorType.BollingerBandsPercentBMomentumBreakout => SignalType.Signal,
|
IndicatorType.BollingerBandsPercentBMomentumBreakout => SignalType.Signal,
|
||||||
|
IndicatorType.IchimokuKumoTrend => SignalType.Trend,
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,11 @@ const CustomScenario: React.FC<ICustomScenario> = ({
|
|||||||
case IndicatorType.LaggingStc:
|
case IndicatorType.LaggingStc:
|
||||||
params = ['cyclePeriods', 'fastPeriods', 'slowPeriods'];
|
params = ['cyclePeriods', 'fastPeriods', 'slowPeriods'];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IndicatorType.IchimokuKumoTrend:
|
||||||
|
params = ['tenkanPeriods', 'kijunPeriods', 'senkouBPeriods', 'offsetPeriods'];
|
||||||
|
break;
|
||||||
|
|
||||||
case IndicatorType.Composite:
|
case IndicatorType.Composite:
|
||||||
params = []; // Composite might not need specific parameters
|
params = []; // Composite might not need specific parameters
|
||||||
break;
|
break;
|
||||||
@@ -123,6 +127,9 @@ const CustomScenario: React.FC<ICustomScenario> = ({
|
|||||||
case IndicatorType.Composite:
|
case IndicatorType.Composite:
|
||||||
label = 'Composite';
|
label = 'Composite';
|
||||||
break;
|
break;
|
||||||
|
case IndicatorType.IchimokuKumoTrend:
|
||||||
|
label = 'Ichimoku Kumo Trend';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
label = type;
|
label = type;
|
||||||
break;
|
break;
|
||||||
@@ -142,7 +149,13 @@ const CustomScenario: React.FC<ICustomScenario> = ({
|
|||||||
multiplier: 3.0,
|
multiplier: 3.0,
|
||||||
stochPeriods: 14,
|
stochPeriods: 14,
|
||||||
smoothPeriods: 3,
|
smoothPeriods: 3,
|
||||||
cyclePeriods: 10
|
cyclePeriods: 10,
|
||||||
|
tenkanPeriods: 9,
|
||||||
|
kijunPeriods: 26,
|
||||||
|
senkouBPeriods: 52,
|
||||||
|
offsetPeriods: 26,
|
||||||
|
senkouOffset: 26,
|
||||||
|
chikouOffset: 26
|
||||||
}
|
}
|
||||||
setIndicators([...indicators, newIndicator])
|
setIndicators([...indicators, newIndicator])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,15 @@ import moment from 'moment'
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import {useEffect, useRef, useState} from 'react'
|
import {useEffect, useRef, useState} from 'react'
|
||||||
|
|
||||||
import type {
|
import {
|
||||||
Candle,
|
Candle,
|
||||||
IndicatorsResultBase,
|
IndicatorsResultBase,
|
||||||
IndicatorType,
|
IndicatorType,
|
||||||
LightSignal,
|
LightSignal,
|
||||||
Position,
|
Position,
|
||||||
|
PositionStatus,
|
||||||
|
TradeDirection,
|
||||||
} from '../../../../generated/ManagingApi'
|
} from '../../../../generated/ManagingApi'
|
||||||
import {PositionStatus, TradeDirection,} from '../../../../generated/ManagingApi'
|
|
||||||
import useTheme from '../../../../hooks/useTheme'
|
import useTheme from '../../../../hooks/useTheme'
|
||||||
|
|
||||||
// var customTheme = {
|
// var customTheme = {
|
||||||
@@ -485,6 +486,47 @@ const TradeChart = ({
|
|||||||
chandelierExitsShortsSeries.setData(chandelierExitsShorts)
|
chandelierExitsShortsSeries.setData(chandelierExitsShorts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display Ichimoku Cloud (Kumo)
|
||||||
|
if (indicatorsValues?.[IndicatorType.IchimokuKumoTrend]?.ichimoku != null) {
|
||||||
|
const senkouSpanASeries = chart.current.addLineSeries({
|
||||||
|
color: theme.secondary,
|
||||||
|
lineWidth: 1,
|
||||||
|
priceLineVisible: false,
|
||||||
|
priceLineWidth: 1,
|
||||||
|
title: 'Senkou Span A',
|
||||||
|
pane: 0,
|
||||||
|
lineStyle: LineStyle.Solid,
|
||||||
|
})
|
||||||
|
|
||||||
|
const senkouSpanAData = indicatorsValues[IndicatorType.IchimokuKumoTrend].ichimoku?.map((w) => {
|
||||||
|
return {
|
||||||
|
time: moment(w.date).unix(),
|
||||||
|
value: w.senkouSpanA,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// @ts-ignore
|
||||||
|
senkouSpanASeries.setData(senkouSpanAData)
|
||||||
|
|
||||||
|
const senkouSpanBSeries = chart.current.addLineSeries({
|
||||||
|
color: theme.accent,
|
||||||
|
lineWidth: 1,
|
||||||
|
priceLineVisible: false,
|
||||||
|
priceLineWidth: 1,
|
||||||
|
title: 'Senkou Span B',
|
||||||
|
pane: 0,
|
||||||
|
lineStyle: LineStyle.Solid,
|
||||||
|
})
|
||||||
|
|
||||||
|
const senkouSpanBData = indicatorsValues[IndicatorType.IchimokuKumoTrend].ichimoku?.map((w) => {
|
||||||
|
return {
|
||||||
|
time: moment(w.date).unix(),
|
||||||
|
value: w.senkouSpanB,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// @ts-ignore
|
||||||
|
senkouSpanBSeries.setData(senkouSpanBData)
|
||||||
|
}
|
||||||
|
|
||||||
// Display Bollinger Bands on price chart
|
// Display Bollinger Bands on price chart
|
||||||
if (indicatorsValues?.BollingerBandsPercentBMomentumBreakout != null) {
|
if (indicatorsValues?.BollingerBandsPercentBMomentumBreakout != null) {
|
||||||
const upperBandSeries = chart.current.addLineSeries({
|
const upperBandSeries = chart.current.addLineSeries({
|
||||||
|
|||||||
@@ -4965,6 +4965,12 @@ export interface LightIndicator {
|
|||||||
cyclePeriods?: number | null;
|
cyclePeriods?: number | null;
|
||||||
kFactor?: number | null;
|
kFactor?: number | null;
|
||||||
dFactor?: number | null;
|
dFactor?: number | null;
|
||||||
|
tenkanPeriods?: number | null;
|
||||||
|
kijunPeriods?: number | null;
|
||||||
|
senkouBPeriods?: number | null;
|
||||||
|
offsetPeriods?: number | null;
|
||||||
|
senkouOffset?: number | null;
|
||||||
|
chikouOffset?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum IndicatorType {
|
export enum IndicatorType {
|
||||||
@@ -4985,6 +4991,7 @@ export enum IndicatorType {
|
|||||||
SuperTrendCrossEma = "SuperTrendCrossEma",
|
SuperTrendCrossEma = "SuperTrendCrossEma",
|
||||||
DualEmaCross = "DualEmaCross",
|
DualEmaCross = "DualEmaCross",
|
||||||
BollingerBandsPercentBMomentumBreakout = "BollingerBandsPercentBMomentumBreakout",
|
BollingerBandsPercentBMomentumBreakout = "BollingerBandsPercentBMomentumBreakout",
|
||||||
|
IchimokuKumoTrend = "IchimokuKumoTrend",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SignalType {
|
export enum SignalType {
|
||||||
@@ -5575,6 +5582,12 @@ export interface IndicatorBase {
|
|||||||
cyclePeriods?: number | null;
|
cyclePeriods?: number | null;
|
||||||
kFactor?: number | null;
|
kFactor?: number | null;
|
||||||
dFactor?: number | null;
|
dFactor?: number | null;
|
||||||
|
tenkanPeriods?: number | null;
|
||||||
|
kijunPeriods?: number | null;
|
||||||
|
senkouBPeriods?: number | null;
|
||||||
|
offsetPeriods?: number | null;
|
||||||
|
senkouOffset?: number | null;
|
||||||
|
chikouOffset?: number | null;
|
||||||
user?: User | null;
|
user?: User | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5606,6 +5619,7 @@ export interface IndicatorsResultBase {
|
|||||||
stdDev?: StdDevResult[] | null;
|
stdDev?: StdDevResult[] | null;
|
||||||
superTrend?: SuperTrendResult[] | null;
|
superTrend?: SuperTrendResult[] | null;
|
||||||
chandelierLong?: ChandelierResult[] | null;
|
chandelierLong?: ChandelierResult[] | null;
|
||||||
|
ichimoku?: IchimokuResult[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResultBase {
|
export interface ResultBase {
|
||||||
@@ -5682,6 +5696,14 @@ export interface SuperTrendResult extends ResultBase {
|
|||||||
lowerBand?: number | null;
|
lowerBand?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IchimokuResult extends ResultBase {
|
||||||
|
tenkanSen?: number | null;
|
||||||
|
kijunSen?: number | null;
|
||||||
|
senkouSpanA?: number | null;
|
||||||
|
senkouSpanB?: number | null;
|
||||||
|
chikouSpan?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GetCandlesWithIndicatorsRequest {
|
export interface GetCandlesWithIndicatorsRequest {
|
||||||
ticker?: Ticker;
|
ticker?: Ticker;
|
||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
|
|||||||
@@ -431,6 +431,12 @@ export interface LightIndicator {
|
|||||||
cyclePeriods?: number | null;
|
cyclePeriods?: number | null;
|
||||||
kFactor?: number | null;
|
kFactor?: number | null;
|
||||||
dFactor?: number | null;
|
dFactor?: number | null;
|
||||||
|
tenkanPeriods?: number | null;
|
||||||
|
kijunPeriods?: number | null;
|
||||||
|
senkouBPeriods?: number | null;
|
||||||
|
offsetPeriods?: number | null;
|
||||||
|
senkouOffset?: number | null;
|
||||||
|
chikouOffset?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum IndicatorType {
|
export enum IndicatorType {
|
||||||
@@ -451,6 +457,7 @@ export enum IndicatorType {
|
|||||||
SuperTrendCrossEma = "SuperTrendCrossEma",
|
SuperTrendCrossEma = "SuperTrendCrossEma",
|
||||||
DualEmaCross = "DualEmaCross",
|
DualEmaCross = "DualEmaCross",
|
||||||
BollingerBandsPercentBMomentumBreakout = "BollingerBandsPercentBMomentumBreakout",
|
BollingerBandsPercentBMomentumBreakout = "BollingerBandsPercentBMomentumBreakout",
|
||||||
|
IchimokuKumoTrend = "IchimokuKumoTrend",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SignalType {
|
export enum SignalType {
|
||||||
@@ -1041,6 +1048,12 @@ export interface IndicatorBase {
|
|||||||
cyclePeriods?: number | null;
|
cyclePeriods?: number | null;
|
||||||
kFactor?: number | null;
|
kFactor?: number | null;
|
||||||
dFactor?: number | null;
|
dFactor?: number | null;
|
||||||
|
tenkanPeriods?: number | null;
|
||||||
|
kijunPeriods?: number | null;
|
||||||
|
senkouBPeriods?: number | null;
|
||||||
|
offsetPeriods?: number | null;
|
||||||
|
senkouOffset?: number | null;
|
||||||
|
chikouOffset?: number | null;
|
||||||
user?: User | null;
|
user?: User | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1072,6 +1085,7 @@ export interface IndicatorsResultBase {
|
|||||||
stdDev?: StdDevResult[] | null;
|
stdDev?: StdDevResult[] | null;
|
||||||
superTrend?: SuperTrendResult[] | null;
|
superTrend?: SuperTrendResult[] | null;
|
||||||
chandelierLong?: ChandelierResult[] | null;
|
chandelierLong?: ChandelierResult[] | null;
|
||||||
|
ichimoku?: IchimokuResult[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResultBase {
|
export interface ResultBase {
|
||||||
@@ -1148,6 +1162,14 @@ export interface SuperTrendResult extends ResultBase {
|
|||||||
lowerBand?: number | null;
|
lowerBand?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IchimokuResult extends ResultBase {
|
||||||
|
tenkanSen?: number | null;
|
||||||
|
kijunSen?: number | null;
|
||||||
|
senkouSpanA?: number | null;
|
||||||
|
senkouSpanB?: number | null;
|
||||||
|
chikouSpan?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GetCandlesWithIndicatorsRequest {
|
export interface GetCandlesWithIndicatorsRequest {
|
||||||
ticker?: Ticker;
|
ticker?: Ticker;
|
||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ const ALL_INDICATORS = [
|
|||||||
IndicatorType.DualEmaCross,
|
IndicatorType.DualEmaCross,
|
||||||
IndicatorType.StochasticCross,
|
IndicatorType.StochasticCross,
|
||||||
IndicatorType.BollingerBandsPercentBMomentumBreakout,
|
IndicatorType.BollingerBandsPercentBMomentumBreakout,
|
||||||
|
IndicatorType.IchimokuKumoTrend,
|
||||||
]
|
]
|
||||||
|
|
||||||
// Indicator type to parameter mapping
|
// Indicator type to parameter mapping
|
||||||
@@ -125,6 +126,7 @@ const INDICATOR_PARAM_MAPPING = {
|
|||||||
[IndicatorType.StochasticCross]: ['stochPeriods', 'signalPeriods', 'smoothPeriods', 'kFactor', 'dFactor'],
|
[IndicatorType.StochasticCross]: ['stochPeriods', 'signalPeriods', 'smoothPeriods', 'kFactor', 'dFactor'],
|
||||||
[IndicatorType.Stc]: ['cyclePeriods', 'fastPeriods', 'slowPeriods'],
|
[IndicatorType.Stc]: ['cyclePeriods', 'fastPeriods', 'slowPeriods'],
|
||||||
[IndicatorType.LaggingStc]: ['cyclePeriods', 'fastPeriods', 'slowPeriods'],
|
[IndicatorType.LaggingStc]: ['cyclePeriods', 'fastPeriods', 'slowPeriods'],
|
||||||
|
[IndicatorType.IchimokuKumoTrend]: ['tenkanPeriods', 'kijunPeriods', 'senkouBPeriods', 'offsetPeriods', 'senkouOffset', 'chikouOffset'],
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ const ALL_INDICATORS = [
|
|||||||
IndicatorType.DualEmaCross,
|
IndicatorType.DualEmaCross,
|
||||||
IndicatorType.StochasticCross,
|
IndicatorType.StochasticCross,
|
||||||
IndicatorType.BollingerBandsPercentBMomentumBreakout,
|
IndicatorType.BollingerBandsPercentBMomentumBreakout,
|
||||||
|
IndicatorType.IchimokuKumoTrend,
|
||||||
]
|
]
|
||||||
|
|
||||||
// Form Interface
|
// Form Interface
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ interface IIndicatorFormInput {
|
|||||||
stochPeriods: number
|
stochPeriods: number
|
||||||
smoothPeriods: number
|
smoothPeriods: number
|
||||||
cyclePeriods: number
|
cyclePeriods: number
|
||||||
|
tenkanPeriods: number
|
||||||
|
kijunPeriods: number
|
||||||
|
senkouBPeriods: number
|
||||||
|
offsetPeriods: number
|
||||||
|
senkouOffset: number
|
||||||
|
chikouOffset: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const IndicatorList: React.FC = () => {
|
const IndicatorList: React.FC = () => {
|
||||||
@@ -30,7 +36,16 @@ const IndicatorList: React.FC = () => {
|
|||||||
)
|
)
|
||||||
const [indicators, setIndicators] = useState<IndicatorViewModel[]>([])
|
const [indicators, setIndicators] = useState<IndicatorViewModel[]>([])
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
const { register, handleSubmit } = useForm<IIndicatorFormInput>()
|
const { register, handleSubmit } = useForm<IIndicatorFormInput>({
|
||||||
|
defaultValues: {
|
||||||
|
tenkanPeriods: 9,
|
||||||
|
kijunPeriods: 26,
|
||||||
|
senkouBPeriods: 52,
|
||||||
|
offsetPeriods: 26,
|
||||||
|
senkouOffset: 26,
|
||||||
|
chikouOffset: 26
|
||||||
|
}
|
||||||
|
})
|
||||||
const { apiUrl } = useApiUrlStore()
|
const { apiUrl } = useApiUrlStore()
|
||||||
const scenarioClient = new ScenarioClient({}, apiUrl)
|
const scenarioClient = new ScenarioClient({}, apiUrl)
|
||||||
|
|
||||||
@@ -47,7 +62,13 @@ const IndicatorList: React.FC = () => {
|
|||||||
form.multiplier,
|
form.multiplier,
|
||||||
form.stochPeriods,
|
form.stochPeriods,
|
||||||
form.smoothPeriods,
|
form.smoothPeriods,
|
||||||
form.cyclePeriods
|
form.cyclePeriods,
|
||||||
|
form.tenkanPeriods,
|
||||||
|
form.kijunPeriods,
|
||||||
|
form.senkouBPeriods,
|
||||||
|
form.offsetPeriods,
|
||||||
|
form.senkouOffset,
|
||||||
|
form.chikouOffset
|
||||||
)
|
)
|
||||||
.then((indicator: IndicatorViewModel) => {
|
.then((indicator: IndicatorViewModel) => {
|
||||||
t.update('success', 'Indicator created')
|
t.update('success', 'Indicator created')
|
||||||
@@ -497,6 +518,102 @@ const IndicatorList: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{indicatorType == IndicatorType.IchimokuKumoTrend ? (
|
||||||
|
<>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="tenkanPeriods" className="label mr-6">
|
||||||
|
Tenkan Periods
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="9"
|
||||||
|
className="input"
|
||||||
|
{...register('tenkanPeriods')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="kijunPeriods" className="label mr-6">
|
||||||
|
Kijun Periods
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="26"
|
||||||
|
className="input"
|
||||||
|
{...register('kijunPeriods')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="senkouBPeriods" className="label mr-6">
|
||||||
|
Senkou B Periods
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="52"
|
||||||
|
className="input"
|
||||||
|
{...register('senkouBPeriods')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="offsetPeriods" className="label mr-6">
|
||||||
|
Offset Periods
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="26"
|
||||||
|
className="input"
|
||||||
|
{...register('offsetPeriods')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="senkouOffset" className="label mr-6">
|
||||||
|
Senkou Offset
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="26"
|
||||||
|
className="input"
|
||||||
|
{...register('senkouOffset')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="chikouOffset" className="label mr-6">
|
||||||
|
Chikou Offset
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="26"
|
||||||
|
className="input"
|
||||||
|
{...register('chikouOffset')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<div className="modal-action">
|
<div className="modal-action">
|
||||||
<button type="submit" className="btn">
|
<button type="submit" className="btn">
|
||||||
Build
|
Build
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ interface IIndicatorFormInput {
|
|||||||
stochPeriods: number
|
stochPeriods: number
|
||||||
smoothPeriods: number
|
smoothPeriods: number
|
||||||
cyclePeriods: number
|
cyclePeriods: number
|
||||||
|
tenkanPeriods: number
|
||||||
|
kijunPeriods: number
|
||||||
|
senkouBPeriods: number
|
||||||
|
offsetPeriods: number
|
||||||
|
senkouOffset: number
|
||||||
|
chikouOffset: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const IndicatorList: React.FC = () => {
|
const IndicatorList: React.FC = () => {
|
||||||
@@ -30,7 +36,16 @@ const IndicatorList: React.FC = () => {
|
|||||||
)
|
)
|
||||||
const [indicators, setIndicators] = useState<Indicator[]>([])
|
const [indicators, setIndicators] = useState<Indicator[]>([])
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
const { register, handleSubmit } = useForm<IIndicatorFormInput>()
|
const { register, handleSubmit } = useForm<IIndicatorFormInput>({
|
||||||
|
defaultValues: {
|
||||||
|
tenkanPeriods: 9,
|
||||||
|
kijunPeriods: 26,
|
||||||
|
senkouBPeriods: 52,
|
||||||
|
offsetPeriods: 26,
|
||||||
|
senkouOffset: 26,
|
||||||
|
chikouOffset: 26
|
||||||
|
}
|
||||||
|
})
|
||||||
const { apiUrl } = useApiUrlStore()
|
const { apiUrl } = useApiUrlStore()
|
||||||
const scenarioClient = new ScenarioClient({}, apiUrl)
|
const scenarioClient = new ScenarioClient({}, apiUrl)
|
||||||
|
|
||||||
@@ -47,7 +62,13 @@ const IndicatorList: React.FC = () => {
|
|||||||
form.multiplier,
|
form.multiplier,
|
||||||
form.stochPeriods,
|
form.stochPeriods,
|
||||||
form.smoothPeriods,
|
form.smoothPeriods,
|
||||||
form.cyclePeriods
|
form.cyclePeriods,
|
||||||
|
form.tenkanPeriods,
|
||||||
|
form.kijunPeriods,
|
||||||
|
form.senkouBPeriods,
|
||||||
|
form.offsetPeriods,
|
||||||
|
form.senkouOffset,
|
||||||
|
form.chikouOffset
|
||||||
)
|
)
|
||||||
.then((indicator: Indicator) => {
|
.then((indicator: Indicator) => {
|
||||||
t.update('success', 'Indicator created')
|
t.update('success', 'Indicator created')
|
||||||
@@ -413,6 +434,102 @@ const IndicatorList: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{indicatorType == IndicatorType.IchimokuKumoTrend ? (
|
||||||
|
<>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="tenkanPeriods" className="label mr-6">
|
||||||
|
Tenkan Periods
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="9"
|
||||||
|
className="input"
|
||||||
|
{...register('tenkanPeriods')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="kijunPeriods" className="label mr-6">
|
||||||
|
Kijun Periods
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="26"
|
||||||
|
className="input"
|
||||||
|
{...register('kijunPeriods')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="senkouBPeriods" className="label mr-6">
|
||||||
|
Senkou B Periods
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="52"
|
||||||
|
className="input"
|
||||||
|
{...register('senkouBPeriods')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="offsetPeriods" className="label mr-6">
|
||||||
|
Offset Periods
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="26"
|
||||||
|
className="input"
|
||||||
|
{...register('offsetPeriods')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="senkouOffset" className="label mr-6">
|
||||||
|
Senkou Offset
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="26"
|
||||||
|
className="input"
|
||||||
|
{...register('senkouOffset')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<label htmlFor="chikouOffset" className="label mr-6">
|
||||||
|
Chikou Offset
|
||||||
|
</label>
|
||||||
|
<label className="input-group">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="26"
|
||||||
|
className="input"
|
||||||
|
{...register('chikouOffset')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<div className="modal-action">
|
<div className="modal-action">
|
||||||
<button type="submit" className="btn">
|
<button type="submit" className="btn">
|
||||||
Build
|
Build
|
||||||
|
|||||||
Reference in New Issue
Block a user