diff --git a/src/Managing.Api/Controllers/BacktestController.cs b/src/Managing.Api/Controllers/BacktestController.cs index 857ca31..b629fd5 100644 --- a/src/Managing.Api/Controllers/BacktestController.cs +++ b/src/Managing.Api/Controllers/BacktestController.cs @@ -171,7 +171,8 @@ public class BacktestController : BaseController FlipPosition = request.Config.FlipPosition, Name = request.Config.Name ?? $"Backtest-{request.Config.ScenarioName}-{DateTime.UtcNow:yyyyMMdd-HHmmss}", - CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable + CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable, + Scenario = scenario, }; switch (request.Config.BotType) diff --git a/src/Managing.Application.Tests/BotsTests.cs b/src/Managing.Application.Tests/BotsTests.cs index 4fd2f87..82bb7ca 100644 --- a/src/Managing.Application.Tests/BotsTests.cs +++ b/src/Managing.Application.Tests/BotsTests.cs @@ -1084,8 +1084,8 @@ namespace Managing.Application.Tests var scenarioName = string.Join("_", paramCombo.Select(p => $"{p.strategyConfig.Name}_{p.parameterSet.Name}")); var scenario = BuildScenario(scenarioName, paramCombo); - scenarios.Add(scenario); scenario.LoopbackPeriod = 15; + scenarios.Add(scenario); } } diff --git a/src/Managing.WebApp/src/components/organism/ScenarioModal/index.tsx b/src/Managing.WebApp/src/components/organism/ScenarioModal/index.tsx index 83eba07..5062a2f 100644 --- a/src/Managing.WebApp/src/components/organism/ScenarioModal/index.tsx +++ b/src/Managing.WebApp/src/components/organism/ScenarioModal/index.tsx @@ -1,14 +1,14 @@ import React, {useEffect} from 'react' import {SubmitHandler, useForm} from 'react-hook-form' import {Modal} from '../../mollecules' -import type {Scenario, Strategy} from '../../../generated/ManagingApi' +import type {Indicator, Scenario} from '../../../generated/ManagingApi' import type {IScenarioFormInput} from '../../../global/type' interface ScenarioModalProps { showModal: boolean onClose: () => void onSubmit: (data: IScenarioFormInput) => Promise - strategies: Strategy[] + indicators: Indicator[] scenario?: Scenario | null // For update mode isUpdate?: boolean } @@ -17,7 +17,7 @@ const ScenarioModal: React.FC = ({ showModal, onClose, onSubmit, - strategies, + indicators, scenario = null, isUpdate = false }) => { @@ -30,7 +30,7 @@ const ScenarioModal: React.FC = ({ // Pre-populate form for update setValue('name', scenario.name || '') setValue('loopbackPeriod', scenario.loopbackPeriod || 0) - setValue('strategies', scenario.strategies?.map(s => s.name || '') || []) + setValue('indicators', scenario.indicators?.map(s => s.name || '') || []) } else { // Reset form for create reset() @@ -68,15 +68,15 @@ const ScenarioModal: React.FC = ({
-
+
+
+
+ + +
+
+
+
+ + +
+
+ + {indicatorType == IndicatorType.EmaTrend || + indicatorType == IndicatorType.RsiDivergence || + indicatorType == IndicatorType.StDev || + indicatorType == IndicatorType.EmaCross || + indicatorType == IndicatorType.RsiDivergenceConfirm ? ( + <> +
+
+ + +
+
+ + ) : null} + + {indicatorType == IndicatorType.MacdCross ? ( + <> +
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ + ) : null} + + {indicatorType == IndicatorType.DualEmaCross ? ( + <> +
+
+ + +
+
+
+
+ + +
+
+ + ) : null} + + {indicatorType == IndicatorType.Stc || indicatorType == IndicatorType.LaggingStc ? ( + <> +
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ + ) : null} + + {indicatorType == IndicatorType.SuperTrend || + indicatorType == IndicatorType.SuperTrendCrossEma || + indicatorType == IndicatorType.ChandelierExit ? ( + <> +
+
+ + +
+
+
+
+ + +
+
+ + ) : null} + + {indicatorType == IndicatorType.StochRsiTrend ? ( + <> +
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ + ) : null} +
+ +
+ + + + + + ) : null} + + + ) +} + +export default IndicatorList diff --git a/src/Managing.WebApp/src/pages/scenarioPage/strategyTable.tsx b/src/Managing.WebApp/src/pages/scenarioPage/indicatorTable.tsx similarity index 65% rename from src/Managing.WebApp/src/pages/scenarioPage/strategyTable.tsx rename to src/Managing.WebApp/src/pages/scenarioPage/indicatorTable.tsx index 6743d86..27471ce 100644 --- a/src/Managing.WebApp/src/pages/scenarioPage/strategyTable.tsx +++ b/src/Managing.WebApp/src/pages/scenarioPage/indicatorTable.tsx @@ -1,27 +1,27 @@ -import { TrashIcon } from '@heroicons/react/solid' -import React, { useEffect, useState } from 'react' +import {TrashIcon} from '@heroicons/react/solid' +import React, {useEffect, useState} from 'react' import useApiUrlStore from '../../app/store/apiStore' -import { SelectColumnFilter, Table, Toast } from '../../components/mollecules' -import type { Strategy } from '../../generated/ManagingApi' -import { ScenarioClient } from '../../generated/ManagingApi' +import {SelectColumnFilter, Table, Toast} from '../../components/mollecules' +import type {Indicator} from '../../generated/ManagingApi' +import {ScenarioClient} from '../../generated/ManagingApi' -interface IStrategyList { - list: Strategy[] +interface IIndicatorList { + list: Indicator[] } -const StrategyTable: React.FC = ({ list }) => { - const [rows, setRows] = useState([]) +const IndicatorTable: React.FC = ({ list }) => { + const [rows, setRows] = useState([]) const { apiUrl } = useApiUrlStore() - async function deleteBacktest(id: string) { - const t = new Toast('Deleting strategy') + async function deleteIndicator(name: string) { + const t = new Toast('Deleting indicator') const client = new ScenarioClient({}, apiUrl) await client - .scenario_DeleteStrategy(id) + .scenario_DeleteIndicator(name) .then(() => { - t.update('info', 'Strategy deleted') + t.update('info', 'Indicator deleted') }) .catch((err) => { t.update('error', err) @@ -62,10 +62,10 @@ const StrategyTable: React.FC = ({ list }) => { { Cell: ({ cell }: any) => ( <> -
+
@@ -91,4 +91,4 @@ const StrategyTable: React.FC = ({ list }) => { ) } -export default StrategyTable +export default IndicatorTable diff --git a/src/Managing.WebApp/src/pages/scenarioPage/scenario.tsx b/src/Managing.WebApp/src/pages/scenarioPage/scenario.tsx index 9bb18b2..07c7141 100644 --- a/src/Managing.WebApp/src/pages/scenarioPage/scenario.tsx +++ b/src/Managing.WebApp/src/pages/scenarioPage/scenario.tsx @@ -1,11 +1,11 @@ -import React, { useEffect, useState } from 'react' +import React, {useEffect, useState} from 'react' import 'react-toastify/dist/ReactToastify.css' -import { Tabs } from '../../components/mollecules' -import type { TabsType } from '../../global/type' +import {Tabs} from '../../components/mollecules' +import type {TabsType} from '../../global/type' import ScenarioList from './scenarioList' -import StrategyList from './strategyList' +import IndicatorList from './indicatorList' // Tabs Array const tabs: TabsType = [ @@ -15,7 +15,7 @@ const tabs: TabsType = [ label: 'Scenarios', }, { - Component: StrategyList, + Component: IndicatorList, index: 2, label: 'Strategies', }, diff --git a/src/Managing.WebApp/src/pages/scenarioPage/scenarioList.tsx b/src/Managing.WebApp/src/pages/scenarioPage/scenarioList.tsx index bd5c6e7..42199e7 100644 --- a/src/Managing.WebApp/src/pages/scenarioPage/scenarioList.tsx +++ b/src/Managing.WebApp/src/pages/scenarioPage/scenarioList.tsx @@ -4,14 +4,14 @@ import 'react-toastify/dist/ReactToastify.css' import useApiUrlStore from '../../app/store/apiStore' import {Toast} from '../../components/mollecules' import {ScenarioModal} from '../../components/organism' -import type {Scenario, Strategy} from '../../generated/ManagingApi' +import type {Indicator, Scenario} from '../../generated/ManagingApi' import {ScenarioClient} from '../../generated/ManagingApi' import type {IScenarioFormInput} from '../../global/type' import ScenarioTable from './scenarioTable' const ScenarioList: React.FC = () => { - const [strategies, setStrategies] = useState([]) + const [indicators, setIndicators] = useState([]) const [scenarios, setScenarios] = useState([]) const [showModal, setShowModal] = useState(false) const { apiUrl } = useApiUrlStore() @@ -20,7 +20,7 @@ const ScenarioList: React.FC = () => { async function createScenario(form: IScenarioFormInput) { const t = new Toast('Creating scenario') await client - .scenario_CreateScenario(form.name, form.loopbackPeriod, form.strategies) + .scenario_CreateScenario(form.name, form.loopbackPeriod, form.indicators) .then((data: Scenario) => { t.update('success', 'Scenario created') setScenarios((arr) => [...arr, data]) @@ -38,8 +38,8 @@ const ScenarioList: React.FC = () => { client.scenario_GetScenarios().then((data) => { setScenarios(data) }) - client.scenario_GetStrategies().then((data) => { - setStrategies(data) + client.scenario_GetIndicators().then((data) => { + setIndicators(data) }) }, []) @@ -56,12 +56,12 @@ const ScenarioList: React.FC = () => { - +
diff --git a/src/Managing.WebApp/src/pages/scenarioPage/scenarioTable.tsx b/src/Managing.WebApp/src/pages/scenarioPage/scenarioTable.tsx index 7359282..e9b8f87 100644 --- a/src/Managing.WebApp/src/pages/scenarioPage/scenarioTable.tsx +++ b/src/Managing.WebApp/src/pages/scenarioPage/scenarioTable.tsx @@ -4,11 +4,11 @@ import React, {useEffect, useState} from 'react' import useApiUrlStore from '../../app/store/apiStore' import {Table, Toast} from '../../components/mollecules' import {ScenarioModal} from '../../components/organism' -import type {Scenario, Strategy} from '../../generated/ManagingApi' +import type {Indicator, Scenario} from '../../generated/ManagingApi' import {ScenarioClient} from '../../generated/ManagingApi' import type {IScenarioFormInput, IScenarioList} from '../../global/type' -const ScenarioTable: React.FC = ({ list, strategies = [], setScenarios }) => { +const ScenarioTable: React.FC = ({ list, indicators = [], setScenarios }) => { const [rows, setRows] = useState([]) const [showUpdateModal, setShowUpdateModal] = useState(false) const [selectedScenario, setSelectedScenario] = useState(null) @@ -38,7 +38,7 @@ const ScenarioTable: React.FC = ({ list, strategies = [], setScen const t = new Toast('Updating scenario') await client - .scenario_UpdateScenario(form.name, form.loopbackPeriod, form.strategies) + .scenario_UpdateScenario(form.name, form.loopbackPeriod, form.indicators) .then(() => { t.update('success', 'Scenario updated') // Refetch scenarios after update since the API returns FileResponse @@ -77,18 +77,18 @@ const ScenarioTable: React.FC = ({ list, strategies = [], setScen { Cell: ({ cell }: any) => ( <> - {cell.row.values.strategies.map((strategy: Strategy) => ( + {cell.row.values.indicators.map((indicator: Indicator) => (
- {strategy.name} + {indicator.name}
))} ), - Header: 'Strategies', - accessor: 'strategies', + Header: 'Indicators', + accessor: 'indicators', disableFilters: true, }, { @@ -135,7 +135,7 @@ const ScenarioTable: React.FC = ({ list, strategies = [], setScen showModal={showUpdateModal} onClose={closeUpdateModal} onSubmit={handleUpdateSubmit} - strategies={strategies} + indicators={indicators} scenario={selectedScenario} isUpdate={true} /> diff --git a/src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx b/src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx index 9b2d5a7..6008f0d 100644 --- a/src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx +++ b/src/Managing.WebApp/src/pages/scenarioPage/strategyList.tsx @@ -5,13 +5,13 @@ import 'react-toastify/dist/ReactToastify.css' import useApiUrlStore from '../../app/store/apiStore' import {Toast} from '../../components/mollecules' -import type {Strategy} from '../../generated/ManagingApi' -import {ScenarioClient, StrategyType, Timeframe,} from '../../generated/ManagingApi' +import type {Indicator} from '../../generated/ManagingApi' +import {IndicatorType, ScenarioClient, Timeframe,} from '../../generated/ManagingApi' -import StrategyTable from './strategyTable' +import IndicatorTable from './indicatorTable' -interface IStrategyFormInput { - type: StrategyType +interface IIndicatorFormInput { + type: IndicatorType timeframe: Timeframe name: string period: number @@ -24,20 +24,20 @@ interface IStrategyFormInput { cyclePeriods: number } -const StrategyList: React.FC = () => { - const [strategyType, setStrategyType] = useState( - StrategyType.RsiDivergence +const IndicatorList: React.FC = () => { + const [indicatorType, setIndicatorType] = useState( + IndicatorType.RsiDivergence ) - const [strategies, setStrategies] = useState([]) + const [indicators, setIndicators] = useState([]) const [showModal, setShowModal] = useState(false) - const { register, handleSubmit } = useForm() + const { register, handleSubmit } = useForm() const { apiUrl } = useApiUrlStore() const scenarioClient = new ScenarioClient({}, apiUrl) - async function createStrategy(form: IStrategyFormInput) { - const t = new Toast('Creating strategy') + async function createIndicator(form: IIndicatorFormInput) { + const t = new Toast('Creating indicator') await scenarioClient - .scenario_CreateStrategy( + .scenario_CreateIndicator( form.type, form.name, form.period, @@ -49,27 +49,27 @@ const StrategyList: React.FC = () => { form.smoothPeriods, form.cyclePeriods ) - .then((strategy: Strategy) => { - t.update('success', 'Strategy created') - setStrategies((arr) => [...arr, strategy]) + .then((indicator: Indicator) => { + t.update('success', 'Indicator created') + setIndicators((arr) => [...arr, indicator]) }) - .catch((err) => { + .catch((err: any) => { t.update('error', err) }) } - function setStrategyTypeEvent(e: any) { - setStrategyType(e.target.value) + function setIndicatorTypeEvent(e: any) { + setIndicatorType(e.target.value) } - const onSubmit: SubmitHandler = async (form) => { + const onSubmit: SubmitHandler = async (form) => { closeModal() - await createStrategy(form) + await createIndicator(form) } useEffect(() => { - scenarioClient.scenario_GetStrategies().then((data) => { - setStrategies(data) + scenarioClient.scenario_GetIndicators().then((data: Indicator[]) => { + setIndicators(data) }) }, []) @@ -85,9 +85,9 @@ const StrategyList: React.FC = () => {
- + {showModal ? ( <>
@@ -101,7 +101,7 @@ const StrategyList: React.FC = () => { ✕
- Strategy builder + Indicator builder
@@ -123,11 +123,11 @@ const StrategyList: React.FC = () => { className="select w-full max-w-xs" {...register('type', { onChange: (e) => { - setStrategyTypeEvent(e) + setIndicatorTypeEvent(e) }, })} > - {Object.keys(StrategyType).map((item) => ( + {Object.keys(IndicatorType).map((item) => ( @@ -153,11 +153,11 @@ const StrategyList: React.FC = () => {
- {strategyType == StrategyType.EmaTrend || - strategyType == StrategyType.RsiDivergence || - strategyType == StrategyType.StDev || - strategyType == StrategyType.EmaCross || - strategyType == StrategyType.RsiDivergenceConfirm ? ( + {indicatorType == IndicatorType.EmaTrend || + indicatorType == IndicatorType.RsiDivergence || + indicatorType == IndicatorType.StDev || + indicatorType == IndicatorType.EmaCross || + indicatorType == IndicatorType.RsiDivergenceConfirm ? ( <>
@@ -177,7 +177,7 @@ const StrategyList: React.FC = () => { ) : null} - {strategyType == StrategyType.MacdCross ? ( + {indicatorType == IndicatorType.MacdCross ? ( <>
@@ -227,7 +227,7 @@ const StrategyList: React.FC = () => { ) : null} - {strategyType == StrategyType.DualEmaCross ? ( + {indicatorType == IndicatorType.DualEmaCross ? ( <>
@@ -262,7 +262,7 @@ const StrategyList: React.FC = () => { ) : null} - {strategyType == StrategyType.Stc || strategyType == StrategyType.LaggingStc ? ( + {indicatorType == IndicatorType.Stc || indicatorType == IndicatorType.LaggingStc ? ( <>
@@ -312,9 +312,9 @@ const StrategyList: React.FC = () => { ) : null} - {strategyType == StrategyType.SuperTrend || - strategyType == StrategyType.SuperTrendCrossEma || - strategyType == StrategyType.ChandelierExit ? ( + {indicatorType == IndicatorType.SuperTrend || + indicatorType == IndicatorType.SuperTrendCrossEma || + indicatorType == IndicatorType.ChandelierExit ? ( <>
@@ -349,7 +349,7 @@ const StrategyList: React.FC = () => { ) : null} - {strategyType == StrategyType.StochRsiTrend ? ( + {indicatorType == IndicatorType.StochRsiTrend ? ( <>
@@ -429,4 +429,4 @@ const StrategyList: React.FC = () => { ) } -export default StrategyList +export default IndicatorList