From 88f195c0ca7ce93dc9fe0296fac5f06ea2c20ef6 Mon Sep 17 00:00:00 2001 From: Oda <102867384+CryptoOda@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:00:59 +0700 Subject: [PATCH] Synth api (#28) * Add synthApi * Put confidence for Synth proba * Update the code * Update readme * Fix bootstraping * fix github build * Update the endpoints for scenario * Add scenario and update backtest modal * Update bot modal * Update interfaces for synth * add synth to backtest * Add Kelly criterion and better signal * Update signal confidence * update doc * save leaderboard and prediction * Update nswag to generate ApiClient in the correct path * Unify the trading modal * Save miner and prediction * Update messaging and block new signal until position not close when flipping off * Rename strategies to indicators * Update doc * Update chart + add signal name * Fix signal direction * Update docker webui * remove crypto npm * Clean * Fix mobile a bit --- .../src/app/store/customMoneyManagement.tsx | 6 +- .../src/app/store/customScenario.tsx | 2 +- .../src/components/mollecules/Modal/Modal.tsx | 2 +- .../mollecules/Modal/ModalHeader.tsx | 2 +- .../organism/Backtest/backtestCards.tsx | 27 +++--- .../organism/BotNameModal/BotNameModal.tsx | 2 +- .../CustomScenario/CustomScenario.tsx | 2 +- .../UnifiedTradingModal.tsx | 93 +++++++++++++++++-- src/Managing.WebApp/src/global/type.tsx | 4 +- .../src/pages/botsPage/botList.tsx | 41 +++++--- .../settingsPage/account/accountModal.tsx | 18 ++-- 11 files changed, 141 insertions(+), 58 deletions(-) diff --git a/src/Managing.WebApp/src/app/store/customMoneyManagement.tsx b/src/Managing.WebApp/src/app/store/customMoneyManagement.tsx index 8cad8fc..152500b 100644 --- a/src/Managing.WebApp/src/app/store/customMoneyManagement.tsx +++ b/src/Managing.WebApp/src/app/store/customMoneyManagement.tsx @@ -1,9 +1,9 @@ -import { create } from 'zustand' +import {create} from 'zustand' -import type { MoneyManagement } from '../../generated/ManagingApi' +import type {MoneyManagement} from '../../generated/ManagingApi' type CustomMoneyManagementStore = { - setCustomMoneyManagement: (custom: MoneyManagement) => void + setCustomMoneyManagement: (custom: MoneyManagement | null) => void moneyManagement: MoneyManagement | null } diff --git a/src/Managing.WebApp/src/app/store/customScenario.tsx b/src/Managing.WebApp/src/app/store/customScenario.tsx index 7bde76c..fd31402 100644 --- a/src/Managing.WebApp/src/app/store/customScenario.tsx +++ b/src/Managing.WebApp/src/app/store/customScenario.tsx @@ -3,7 +3,7 @@ import {create} from 'zustand' import type {Scenario} from '../../generated/ManagingApi' type CustomScenarioStore = { - setCustomScenario: (custom: Scenario) => void + setCustomScenario: (custom: Scenario | null) => void scenario: Scenario | null } diff --git a/src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx b/src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx index f6cf771..d29ddb8 100644 --- a/src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx +++ b/src/Managing.WebApp/src/components/mollecules/Modal/Modal.tsx @@ -1,6 +1,6 @@ import React from 'react' -import type {IModalProps} from '../../../global/type' +import type {IModalProps} from '../../../global/type.tsx' import ModalHeader from './ModalHeader' diff --git a/src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx b/src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx index 688ff90..7ea91a7 100644 --- a/src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx +++ b/src/Managing.WebApp/src/components/mollecules/Modal/ModalHeader.tsx @@ -1,6 +1,6 @@ import React from 'react' -import type { IModalProps } from '../../../global/type' +import type {IModalProps} from '../../../global/type.tsx' const ModalHeader: React.FC = ({ onClose, titleHeader }: any) => { return ( diff --git a/src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx b/src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx index 31d62b3..0e6c851 100644 --- a/src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx +++ b/src/Managing.WebApp/src/components/organism/Backtest/backtestCards.tsx @@ -5,14 +5,15 @@ import {useQuery} from '@tanstack/react-query' import useApiUrlStore from '../../../app/store/apiStore' import type { - Backtest, - MoneyManagement, - RunBacktestRequest, - StartBotRequest, - TradingBotConfig + Backtest, + MoneyManagement, + RunBacktestRequest, + StartBotRequest, + TradingBotConfig, + TradingBotConfigRequest } from '../../../generated/ManagingApi' import {BacktestClient, BotClient, MoneyManagementClient} from '../../../generated/ManagingApi' -import type {IBacktestCards} from '../../../global/type' +import type {IBacktestCards} from '../../../global/type.tsx' import MoneyManagementModal from '../../../pages/settingsPage/moneymanagement/moneyManagementModal' import {CardPosition, CardText, Toast} from '../../mollecules' import CardPositionItem from '../Trading/CardPositionItem' @@ -119,9 +120,7 @@ const BacktestCards: React.FC = ({ list, setBacktests }) => { }; const request: StartBotRequest = { - config: tradingBotConfig, - // Only use the money management name if it's not a custom money management - moneyManagementName: isCustomMoneyManagement ? undefined : moneyManagementName + config: tradingBotConfig as unknown as TradingBotConfigRequest, } await client @@ -165,21 +164,19 @@ const BacktestCards: React.FC = ({ list, setBacktests }) => { } const request: RunBacktestRequest = { - config: optimizedConfig, + config: optimizedConfig as unknown as TradingBotConfigRequest, startDate: startDate, endDate: endDate, balance: backtest.walletBalances[0].value, watchOnly: false, save: false, - moneyManagementName: undefined, // We're passing the moneyManagement object directly - moneyManagement: optimizedConfig.moneyManagement } await client .backtest_Run(request) .then((backtest: Backtest) => { t.update('success', `${backtest.config.ticker} Backtest Succeeded`) - setBacktests((arr) => [...arr, backtest]) + setBacktests((arr: Backtest[]) => [...arr, backtest]) }) .catch((err) => { t.update('error', 'Error :' + err) @@ -193,7 +190,7 @@ const BacktestCards: React.FC = ({ list, setBacktests }) => { return (
- {list?.map((backtest: Backtest, index) => ( + {list?.map((backtest: Backtest, index: number) => (
= ({ list, setBacktests }) => { positions={backtest.positions} walletBalances={backtest.walletBalances} signals={backtest.signals} - strategiesValues={backtest.strategiesValues} + indicatorsValues={backtest.indicatorsValues} width={720} height={512} > diff --git a/src/Managing.WebApp/src/components/organism/BotNameModal/BotNameModal.tsx b/src/Managing.WebApp/src/components/organism/BotNameModal/BotNameModal.tsx index ae254ad..ed2b742 100644 --- a/src/Managing.WebApp/src/components/organism/BotNameModal/BotNameModal.tsx +++ b/src/Managing.WebApp/src/components/organism/BotNameModal/BotNameModal.tsx @@ -1,8 +1,8 @@ import React, {useEffect, useState} from 'react' import type {Backtest, MoneyManagement} from '../../../generated/ManagingApi' -import type {IModalProps} from '../../../global/type' import {Modal} from '../../mollecules' +import type {IModalProps} from '../../../global/type.tsx' interface IBotNameModalProps extends IModalProps { backtest: Backtest diff --git a/src/Managing.WebApp/src/components/organism/CustomScenario/CustomScenario.tsx b/src/Managing.WebApp/src/components/organism/CustomScenario/CustomScenario.tsx index 2fa4531..cddde79 100644 --- a/src/Managing.WebApp/src/components/organism/CustomScenario/CustomScenario.tsx +++ b/src/Managing.WebApp/src/components/organism/CustomScenario/CustomScenario.tsx @@ -258,7 +258,7 @@ const CustomScenario: React.FC = ({
{/* Dynamic parameter inputs based on indicator type */} -
+
{getRequiredParams(indicator.type || indicatorTypes[0].type).map((param) => ( = ({ const [selectedTicker, setSelectedTicker] = useState(undefined); const { apiUrl } = useApiUrlStore(); + const { setCustomMoneyManagement: setGlobalCustomMoneyManagement } = useCustomMoneyManagement(); + const { setCustomScenario: setGlobalCustomScenario } = useCustomScenario(); // API clients const scenarioClient = new ScenarioClient({}, apiUrl); @@ -185,6 +189,18 @@ const UnifiedTradingModal: React.FC = ({ setSelectedTicker(backtest.config.ticker); } setValue('scenarioName', backtest.config.scenarioName || ''); + + // Handle custom scenario from backtest + if ((backtest.config as any).scenario) { + setShowCustomScenario(true); + setCustomScenario((backtest.config as any).scenario); + setGlobalCustomScenario((backtest.config as any).scenario); // Also update global store for prefilling + setSelectedScenario('custom'); + } else if (backtest.config.scenarioName) { + setSelectedScenario(backtest.config.scenarioName); + setShowCustomScenario(false); + } + setValue('timeframe', backtest.config.timeframe); setValue('botType', backtest.config.botType); setValue('cooldownPeriod', backtest.config.cooldownPeriod); @@ -203,11 +219,31 @@ const UnifiedTradingModal: React.FC = ({ if (backtest.config.moneyManagement) { setShowCustomMoneyManagement(true); setCustomMoneyManagement(backtest.config.moneyManagement); + + // Convert decimal values to percentages for UI display + const formattedMoneyManagement = { + ...backtest.config.moneyManagement, + stopLoss: backtest.config.moneyManagement.stopLoss * 100, + takeProfit: backtest.config.moneyManagement.takeProfit * 100, + }; + setGlobalCustomMoneyManagement(formattedMoneyManagement); } } else if (mode === 'backtest' && backtest) { // Initialize from existing backtest for re-running setValue('accountName', backtest.config.accountName); setValue('scenarioName', backtest.config.scenarioName || ''); + + // Handle custom scenario from backtest + if ((backtest.config as any).scenario) { + setShowCustomScenario(true); + setCustomScenario((backtest.config as any).scenario); + setGlobalCustomScenario((backtest.config as any).scenario); // Also update global store for prefilling + setSelectedScenario('custom'); + } else if (backtest.config.scenarioName) { + setSelectedScenario(backtest.config.scenarioName); + setShowCustomScenario(false); + } + setValue('timeframe', backtest.config.timeframe); setValue('botType', backtest.config.botType); setValue('cooldownPeriod', backtest.config.cooldownPeriod); @@ -243,6 +279,14 @@ const UnifiedTradingModal: React.FC = ({ if (backtest.config.moneyManagement) { setShowCustomMoneyManagement(true); setCustomMoneyManagement(backtest.config.moneyManagement); + + // Convert decimal values to percentages for UI display + const formattedMoneyManagement = { + ...backtest.config.moneyManagement, + stopLoss: backtest.config.moneyManagement.stopLoss * 100, + takeProfit: backtest.config.moneyManagement.takeProfit * 100, + }; + setGlobalCustomMoneyManagement(formattedMoneyManagement); } // Handle risk management @@ -274,6 +318,33 @@ const UnifiedTradingModal: React.FC = ({ setValue('useForSignalFiltering', config.useForSignalFiltering ?? true); setValue('useForDynamicStopLoss', config.useForDynamicStopLoss ?? true); + // Handle money management - if it exists, treat as custom for update mode + if (config.moneyManagement) { + setShowCustomMoneyManagement(true); + setCustomMoneyManagement(config.moneyManagement); + + // Convert decimal values to percentages for UI display + const formattedMoneyManagement = { + ...config.moneyManagement, + stopLoss: config.moneyManagement.stopLoss * 100, + takeProfit: config.moneyManagement.takeProfit * 100, + }; + setGlobalCustomMoneyManagement(formattedMoneyManagement); + setSelectedMoneyManagement('custom'); // Set dropdown to show "custom" + } + + // Handle scenario - check if we have scenario data or just a name reference + if ((config as any).scenario) { + setShowCustomScenario(true); + setCustomScenario((config as any).scenario); + setGlobalCustomScenario((config as any).scenario); // Also update global store for prefilling + setSelectedScenario('custom'); // Set dropdown to show "custom" + } else if (config.scenarioName) { + setValue('scenarioName', config.scenarioName); + setSelectedScenario(config.scenarioName); + setShowCustomScenario(false); + } + // Handle risk management if (config.riskManagement) { setValue('useCustomRiskManagement', true); @@ -373,9 +444,11 @@ const UnifiedTradingModal: React.FC = ({ if (e.target.value === 'custom') { setShowCustomMoneyManagement(true); setCustomMoneyManagement(undefined); + setGlobalCustomMoneyManagement(null); // Clear global store when creating new custom } else { setShowCustomMoneyManagement(false); setCustomMoneyManagement(undefined); + setGlobalCustomMoneyManagement(null); // Clear global store when switching away from custom setSelectedMoneyManagement(e.target.value); } }; @@ -384,9 +457,11 @@ const UnifiedTradingModal: React.FC = ({ if (e.target.value === 'custom') { setShowCustomScenario(true); setCustomScenario(undefined); + setGlobalCustomScenario(null); // Clear global store when creating new custom } else { setShowCustomScenario(false); setCustomScenario(undefined); + setGlobalCustomScenario(null); // Clear global store when switching away from custom setSelectedScenario(e.target.value); setValue('scenarioName', e.target.value); } @@ -612,7 +687,7 @@ const UnifiedTradingModal: React.FC = ({ )} {/* First Row: Account & Timeframe */} -
+
{moneyManagements.length === 0 ? ( @@ -704,6 +780,7 @@ const UnifiedTradingModal: React.FC = ({ = ({ min="1" max="10" value={selectedLoopQuantity.toString()} - onChange={(e) => setLoopQuantity(Number(e.target.value))} + onChange={(e: React.ChangeEvent) => setLoopQuantity(Number(e.target.value))} /> )} {/* Max Loss Streak & Max Position Time */} -
+
@@ -949,7 +1026,7 @@ const UnifiedTradingModal: React.FC = ({
{/* Trading Options */} -
+
@@ -1179,7 +1256,7 @@ const UnifiedTradingModal: React.FC = ({ {/* Risk Management Parameters Grid */} -
+
@@ -1350,7 +1427,7 @@ const UnifiedTradingModal: React.FC = ({
{/* Checkboxes for Kelly and Expected Utility */} -
+
diff --git a/src/Managing.WebApp/src/global/type.tsx b/src/Managing.WebApp/src/global/type.tsx index db4eb05..b4b0164 100644 --- a/src/Managing.WebApp/src/global/type.tsx +++ b/src/Managing.WebApp/src/global/type.tsx @@ -132,8 +132,8 @@ export type IFormInput = { inline?: boolean } -export type IModalProps = { - showModal?: boolean | undefined +export type IModalProps = { + showModal: boolean onSubmit?: React.FormEventHandler onClose?: React.FormEventHandler titleHeader?: string diff --git a/src/Managing.WebApp/src/pages/botsPage/botList.tsx b/src/Managing.WebApp/src/pages/botsPage/botList.tsx index 5665386..971cb5a 100644 --- a/src/Managing.WebApp/src/pages/botsPage/botList.tsx +++ b/src/Managing.WebApp/src/pages/botsPage/botList.tsx @@ -8,11 +8,11 @@ import TradesModal from '../../components/mollecules/TradesModal/TradesModal' import {TradeChart, UnifiedTradingModal} from '../../components/organism' import type {BotType, MoneyManagement, Position, TradingBotResponse} from '../../generated/ManagingApi' import {BotClient} from '../../generated/ManagingApi' -import type {IBotList} from '../../global/type' +import type {IBotList} from '../../global/type.tsx' import MoneyManagementModal from '../settingsPage/moneymanagement/moneyManagementModal' function baseBadgeClass(isOutlined = false) { - let classes = 'text-xs badge ' + let classes = 'text-xs badge badge-sm transition-all duration-200 hover:scale-105 ' if (isOutlined) { classes += 'badge-outline ' @@ -240,7 +240,7 @@ const BotList: React.FC = ({ list }) => {
- {list.map((bot: TradingBotResponse, index) => ( + {list.map((bot: TradingBotResponse, index: number) => (
= ({ list }) => { }
-

- {bot.config.ticker} - {getMoneyManagementBadge(bot.config.moneyManagement)} - {getIsForWatchingBadge(bot.config.isForWatchingOnly, bot.identifier)} - {getToggleBotStatusBadge(bot.status, bot.identifier, bot.config.botType)} - {getUpdateBotBadge(bot)} - {getManualPositionBadge(bot.identifier)} - {getDeleteBadge(bot.identifier)} -

- +
+ {/* Bot Name - Always on its own line */} +

+ {bot.config.ticker} +

+ + {/* Badge Container - Responsive */} +
+ {/* Info Badges */} +
+ {getMoneyManagementBadge(bot.config.moneyManagement)} + {getIsForWatchingBadge(bot.config.isForWatchingOnly, bot.identifier)} +
+ + {/* Action Badges */} +
+ {getToggleBotStatusBadge(bot.status, bot.identifier, bot.config.botType)} + {getUpdateBotBadge(bot)} + {getManualPositionBadge(bot.identifier)} + {getDeleteBadge(bot.identifier)} +
+
+
diff --git a/src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx b/src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx index 21f3953..437eba7 100644 --- a/src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx +++ b/src/Managing.WebApp/src/pages/settingsPage/account/accountModal.tsx @@ -1,16 +1,12 @@ -import { useState } from 'react' -import type { SubmitHandler } from 'react-hook-form' -import { useForm } from 'react-hook-form' +import {useState} from 'react' +import type {SubmitHandler} from 'react-hook-form' +import {useForm} from 'react-hook-form' import useApiUrlStore from '../../../app/store/apiStore' -import { Modal, Toast } from '../../../components/mollecules' -import type { Account } from '../../../generated/ManagingApi' -import { - AccountType, - AccountClient, - TradingExchanges, -} from '../../../generated/ManagingApi' -import type { IAccountFormInput, IModalProps } from '../../../global/type' +import {Modal, Toast} from '../../../components/mollecules' +import type {Account} from '../../../generated/ManagingApi' +import {AccountClient, AccountType, TradingExchanges,} from '../../../generated/ManagingApi' +import type {IAccountFormInput, IModalProps} from '../../../global/type.tsx' const AccountModal: React.FC = ({ showModal, toggleModal }) => { const [selectedExchange, setSelectedExchange] = useState()