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
This commit is contained in:
Oda
2025-07-03 15:00:59 +07:00
committed by GitHub
parent a547c4a040
commit 88f195c0ca
11 changed files with 141 additions and 58 deletions

View File

@@ -3,7 +3,7 @@ import { create } from 'zustand'
import type {MoneyManagement} from '../../generated/ManagingApi'
type CustomMoneyManagementStore = {
setCustomMoneyManagement: (custom: MoneyManagement) => void
setCustomMoneyManagement: (custom: MoneyManagement | null) => void
moneyManagement: MoneyManagement | null
}

View File

@@ -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
}

View File

@@ -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'

View File

@@ -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<IModalProps> = ({ onClose, titleHeader }: any) => {
return (

View File

@@ -9,10 +9,11 @@ import type {
MoneyManagement,
RunBacktestRequest,
StartBotRequest,
TradingBotConfig
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<IBacktestCards> = ({ 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<IBacktestCards> = ({ 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<IBacktestCards> = ({ list, setBacktests }) => {
return (
<div className="flex flex-wrap m-4 -mx-4">
{list?.map((backtest: Backtest, index) => (
{list?.map((backtest: Backtest, index: number) => (
<div
key={index.toString()}
className="sm:w-1/2 md:w-1/2 xl:w-1/2 w-full p-2"
@@ -213,7 +210,7 @@ const BacktestCards: React.FC<IBacktestCards> = ({ list, setBacktests }) => {
positions={backtest.positions}
walletBalances={backtest.walletBalances}
signals={backtest.signals}
strategiesValues={backtest.strategiesValues}
indicatorsValues={backtest.indicatorsValues}
width={720}
height={512}
></TradeChart>

View File

@@ -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

View File

@@ -258,7 +258,7 @@ const CustomScenario: React.FC<ICustomScenario> = ({
</div>
{/* Dynamic parameter inputs based on indicator type */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
{getRequiredParams(indicator.type || indicatorTypes[0].type).map((param) => (
<FormInput key={param} label={param.charAt(0).toUpperCase() + param.slice(1)} htmlFor={`${param}-${index}`} inline={false}>
<input

View File

@@ -3,6 +3,8 @@ import React, {useEffect, useState} from 'react'
import {type SubmitHandler, useForm} from 'react-hook-form'
import useApiUrlStore from '../../../app/store/apiStore'
import {useCustomMoneyManagement} from '../../../app/store/customMoneyManagement'
import {useCustomScenario} from '../../../app/store/customScenario'
import {
AccountClient,
BacktestClient,
@@ -136,6 +138,8 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
const [selectedTicker, setSelectedTicker] = useState<Ticker | undefined>(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<UnifiedTradingModalProps> = ({
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<UnifiedTradingModalProps> = ({
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<UnifiedTradingModalProps> = ({
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<UnifiedTradingModalProps> = ({
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<UnifiedTradingModalProps> = ({
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<UnifiedTradingModalProps> = ({
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<UnifiedTradingModalProps> = ({
)}
{/* First Row: Account & Timeframe */}
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormInput label="Account" htmlFor="accountName">
<select
className="select select-bordered w-full"
@@ -650,10 +725,11 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
</div>
{/* Second Row: Money Management & Bot Type */}
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormInput label="Money Management" htmlFor="moneyManagement">
<select
className="select select-bordered w-full"
value={selectedMoneyManagement || (showCustomMoneyManagement ? 'custom' : '')}
onChange={onMoneyManagementChange}
>
{moneyManagements.length === 0 ? (
@@ -704,6 +780,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
<select
className="select select-bordered w-full"
{...register('scenarioName')}
value={selectedScenario || (showCustomScenario ? 'custom' : '')}
onChange={onScenarioChange}
>
{scenarios.length === 0 ? (
@@ -854,7 +931,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
{/* Dates for backtests */}
{mode === 'backtest' && (
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormInput label="Start Date" htmlFor="startDate">
<input
type="date"
@@ -899,13 +976,13 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
min="1"
max="10"
value={selectedLoopQuantity.toString()}
onChange={(e) => setLoopQuantity(Number(e.target.value))}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setLoopQuantity(Number(e.target.value))}
/>
</FormInput>
)}
{/* Max Loss Streak & Max Position Time */}
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormInput
label={
<div className="flex items-center gap-2">
@@ -949,7 +1026,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
</div>
{/* Trading Options */}
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormInput
label={
<div className="flex items-center gap-2">
@@ -1179,7 +1256,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
</FormInput>
{/* Risk Management Parameters Grid */}
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormInput
label={
<div className="flex items-center gap-2">
@@ -1350,7 +1427,7 @@ const UnifiedTradingModal: React.FC<UnifiedTradingModalProps> = ({
</div>
{/* Checkboxes for Kelly and Expected Utility */}
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormInput
label={
<div className="flex items-center gap-2">

View File

@@ -133,7 +133,7 @@ export type IFormInput = {
}
export type IModalProps = {
showModal?: boolean | undefined
showModal: boolean
onSubmit?: React.FormEventHandler
onClose?: React.FormEventHandler
titleHeader?: string

View File

@@ -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<IBotList> = ({ list }) => {
</div>
</div>
{list.map((bot: TradingBotResponse, index) => (
{list.map((bot: TradingBotResponse, index: number) => (
<div
key={index.toString()}
className="sm:w-1 md:w-1/2 xl:w-1/2 w-full p-2"
@@ -258,23 +258,36 @@ const BotList: React.FC<IBotList> = ({ list }) => {
}
</figure>
<div className="card-body">
<h2 className="card-title text-sm">
<div className="mb-4">
{/* Bot Name - Always on its own line */}
<h2 className="card-title text-sm mb-3">
{bot.config.ticker}
</h2>
{/* Badge Container - Responsive */}
<div className="flex flex-wrap gap-1 sm:gap-2">
{/* Info Badges */}
<div className="flex flex-wrap gap-1">
{getMoneyManagementBadge(bot.config.moneyManagement)}
{getIsForWatchingBadge(bot.config.isForWatchingOnly, bot.identifier)}
</div>
{/* Action Badges */}
<div className="flex flex-wrap gap-1">
{getToggleBotStatusBadge(bot.status, bot.identifier, bot.config.botType)}
{getUpdateBotBadge(bot)}
{getManualPositionBadge(bot.identifier)}
{getDeleteBadge(bot.identifier)}
</h2>
</div>
</div>
</div>
<div className="columns-2">
<div>
<div>
<CardText
title="Scenario"
content={bot.config.scenarioName}
content={bot.config.scenarioName ?? bot.config.scenario?.name}
></CardText>
</div>
</div>

View File

@@ -5,12 +5,8 @@ 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 {AccountClient, AccountType, TradingExchanges,} from '../../../generated/ManagingApi'
import type {IAccountFormInput, IModalProps} from '../../../global/type.tsx'
const AccountModal: React.FC<IModalProps> = ({ showModal, toggleModal }) => {
const [selectedExchange, setSelectedExchange] = useState<TradingExchanges>()