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:
2025-11-24 19:43:18 +07:00
parent 478dca51e7
commit 4268626897
16 changed files with 686 additions and 11 deletions

View File

@@ -63,7 +63,11 @@ const CustomScenario: React.FC<ICustomScenario> = ({
case IndicatorType.LaggingStc:
params = ['cyclePeriods', 'fastPeriods', 'slowPeriods'];
break;
case IndicatorType.IchimokuKumoTrend:
params = ['tenkanPeriods', 'kijunPeriods', 'senkouBPeriods', 'offsetPeriods'];
break;
case IndicatorType.Composite:
params = []; // Composite might not need specific parameters
break;
@@ -123,6 +127,9 @@ const CustomScenario: React.FC<ICustomScenario> = ({
case IndicatorType.Composite:
label = 'Composite';
break;
case IndicatorType.IchimokuKumoTrend:
label = 'Ichimoku Kumo Trend';
break;
default:
label = type;
break;
@@ -142,7 +149,13 @@ const CustomScenario: React.FC<ICustomScenario> = ({
multiplier: 3.0,
stochPeriods: 14,
smoothPeriods: 3,
cyclePeriods: 10
cyclePeriods: 10,
tenkanPeriods: 9,
kijunPeriods: 26,
senkouBPeriods: 52,
offsetPeriods: 26,
senkouOffset: 26,
chikouOffset: 26
}
setIndicators([...indicators, newIndicator])
}

View File

@@ -14,14 +14,15 @@ import moment from 'moment'
import * as React from 'react'
import {useEffect, useRef, useState} from 'react'
import type {
import {
Candle,
IndicatorsResultBase,
IndicatorType,
LightSignal,
Position,
PositionStatus,
TradeDirection,
} from '../../../../generated/ManagingApi'
import {PositionStatus, TradeDirection,} from '../../../../generated/ManagingApi'
import useTheme from '../../../../hooks/useTheme'
// var customTheme = {
@@ -485,6 +486,47 @@ const TradeChart = ({
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
if (indicatorsValues?.BollingerBandsPercentBMomentumBreakout != null) {
const upperBandSeries = chart.current.addLineSeries({

View File

@@ -4965,6 +4965,12 @@ export interface LightIndicator {
cyclePeriods?: number | null;
kFactor?: 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 {
@@ -4985,6 +4991,7 @@ export enum IndicatorType {
SuperTrendCrossEma = "SuperTrendCrossEma",
DualEmaCross = "DualEmaCross",
BollingerBandsPercentBMomentumBreakout = "BollingerBandsPercentBMomentumBreakout",
IchimokuKumoTrend = "IchimokuKumoTrend",
}
export enum SignalType {
@@ -5575,6 +5582,12 @@ export interface IndicatorBase {
cyclePeriods?: number | null;
kFactor?: 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;
}
@@ -5606,6 +5619,7 @@ export interface IndicatorsResultBase {
stdDev?: StdDevResult[] | null;
superTrend?: SuperTrendResult[] | null;
chandelierLong?: ChandelierResult[] | null;
ichimoku?: IchimokuResult[] | null;
}
export interface ResultBase {
@@ -5682,6 +5696,14 @@ export interface SuperTrendResult extends ResultBase {
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 {
ticker?: Ticker;
startDate?: Date;

View File

@@ -431,6 +431,12 @@ export interface LightIndicator {
cyclePeriods?: number | null;
kFactor?: 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 {
@@ -451,6 +457,7 @@ export enum IndicatorType {
SuperTrendCrossEma = "SuperTrendCrossEma",
DualEmaCross = "DualEmaCross",
BollingerBandsPercentBMomentumBreakout = "BollingerBandsPercentBMomentumBreakout",
IchimokuKumoTrend = "IchimokuKumoTrend",
}
export enum SignalType {
@@ -1041,6 +1048,12 @@ export interface IndicatorBase {
cyclePeriods?: number | null;
kFactor?: 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;
}
@@ -1072,6 +1085,7 @@ export interface IndicatorsResultBase {
stdDev?: StdDevResult[] | null;
superTrend?: SuperTrendResult[] | null;
chandelierLong?: ChandelierResult[] | null;
ichimoku?: IchimokuResult[] | null;
}
export interface ResultBase {
@@ -1148,6 +1162,14 @@ export interface SuperTrendResult extends ResultBase {
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 {
ticker?: Ticker;
startDate?: Date;

View File

@@ -105,6 +105,7 @@ const ALL_INDICATORS = [
IndicatorType.DualEmaCross,
IndicatorType.StochasticCross,
IndicatorType.BollingerBandsPercentBMomentumBreakout,
IndicatorType.IchimokuKumoTrend,
]
// Indicator type to parameter mapping
@@ -125,6 +126,7 @@ const INDICATOR_PARAM_MAPPING = {
[IndicatorType.StochasticCross]: ['stochPeriods', 'signalPeriods', 'smoothPeriods', 'kFactor', 'dFactor'],
[IndicatorType.Stc]: ['cyclePeriods', 'fastPeriods', 'slowPeriods'],
[IndicatorType.LaggingStc]: ['cyclePeriods', 'fastPeriods', 'slowPeriods'],
[IndicatorType.IchimokuKumoTrend]: ['tenkanPeriods', 'kijunPeriods', 'senkouBPeriods', 'offsetPeriods', 'senkouOffset', 'chikouOffset'],
}
// ============================================================================

View File

@@ -44,6 +44,7 @@ const ALL_INDICATORS = [
IndicatorType.DualEmaCross,
IndicatorType.StochasticCross,
IndicatorType.BollingerBandsPercentBMomentumBreakout,
IndicatorType.IchimokuKumoTrend,
]
// Form Interface

View File

@@ -22,6 +22,12 @@ interface IIndicatorFormInput {
stochPeriods: number
smoothPeriods: number
cyclePeriods: number
tenkanPeriods: number
kijunPeriods: number
senkouBPeriods: number
offsetPeriods: number
senkouOffset: number
chikouOffset: number
}
const IndicatorList: React.FC = () => {
@@ -30,7 +36,16 @@ const IndicatorList: React.FC = () => {
)
const [indicators, setIndicators] = useState<IndicatorViewModel[]>([])
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 scenarioClient = new ScenarioClient({}, apiUrl)
@@ -47,7 +62,13 @@ const IndicatorList: React.FC = () => {
form.multiplier,
form.stochPeriods,
form.smoothPeriods,
form.cyclePeriods
form.cyclePeriods,
form.tenkanPeriods,
form.kijunPeriods,
form.senkouBPeriods,
form.offsetPeriods,
form.senkouOffset,
form.chikouOffset
)
.then((indicator: IndicatorViewModel) => {
t.update('success', 'Indicator created')
@@ -497,6 +518,102 @@ const IndicatorList: React.FC = () => {
</div>
</>
) : 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">
<button type="submit" className="btn">
Build

View File

@@ -22,6 +22,12 @@ interface IIndicatorFormInput {
stochPeriods: number
smoothPeriods: number
cyclePeriods: number
tenkanPeriods: number
kijunPeriods: number
senkouBPeriods: number
offsetPeriods: number
senkouOffset: number
chikouOffset: number
}
const IndicatorList: React.FC = () => {
@@ -30,7 +36,16 @@ const IndicatorList: React.FC = () => {
)
const [indicators, setIndicators] = useState<Indicator[]>([])
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 scenarioClient = new ScenarioClient({}, apiUrl)
@@ -47,7 +62,13 @@ const IndicatorList: React.FC = () => {
form.multiplier,
form.stochPeriods,
form.smoothPeriods,
form.cyclePeriods
form.cyclePeriods,
form.tenkanPeriods,
form.kijunPeriods,
form.senkouBPeriods,
form.offsetPeriods,
form.senkouOffset,
form.chikouOffset
)
.then((indicator: Indicator) => {
t.update('success', 'Indicator created')
@@ -413,6 +434,102 @@ const IndicatorList: React.FC = () => {
</div>
</>
) : 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">
<button type="submit" className="btn">
Build