Postgres (#30)

* Add postgres

* Migrate users

* Migrate geneticRequest

* Try to fix Concurrent call

* Fix asyncawait

* Fix async and concurrent

* Migrate backtests

* Add cache for user by address

* Fix backtest migration

* Fix not open connection

* Fix backtest command error

* Fix concurrent

* Fix all concurrency

* Migrate TradingRepo

* Fix scenarios

* Migrate statistic repo

* Save botbackup

* Add settings et moneymanagement

* Add bot postgres

* fix a bit more backups

* Fix bot model

* Fix loading backup

* Remove cache market for read positions

* Add workers to postgre

* Fix workers api

* Reduce get Accounts for workers

* Migrate synth to postgre

* Fix backtest saved

* Remove mongodb

* botservice decorrelation

* Fix tradingbot scope call

* fix tradingbot

* fix concurrent

* Fix scope for genetics

* Fix account over requesting

* Fix bundle backtest worker

* fix a lot of things

* fix tab backtest

* Remove optimized moneymanagement

* Add light signal to not use User and too much property

* Make money management lighter

* insert indicators to awaitable

* Migrate add strategies to await

* Refactor scenario and indicator retrieval to use asynchronous methods throughout the application

* add more async await

* Add services

* Fix and clean

* Fix bot a bit

* Fix bot and add message for cooldown

* Remove fees

* Add script to deploy db

* Update dfeeploy script

* fix script

* Add idempotent script and backup

* finish script migration

* Fix did user and agent name on start bot
This commit is contained in:
Oda
2025-07-27 15:42:17 +02:00
committed by GitHub
parent 361bfbf6e8
commit 422fecea7b
294 changed files with 23953 additions and 7272 deletions

View File

@@ -1,22 +1,32 @@
import { StatusOfflineIcon } from '@heroicons/react/solid'
import type { SubmitHandler } from 'react-hook-form'
import { useForm } from 'react-hook-form'
import { usePrivy, useSignMessage } from '@privy-io/react-auth'
import {StatusOfflineIcon} from '@heroicons/react/solid'
import type {SubmitHandler} from 'react-hook-form'
import {useForm} from 'react-hook-form'
import {usePrivy, useSignMessage} from '@privy-io/react-auth'
import {useEffect} from 'react'
import useApiUrlStore from '../../../app/store/apiStore'
import { UserClient } from '../../../generated/ManagingApi'
import type { ILoginFormInput } from '../../../global/type'
import {UserClient} from '../../../generated/ManagingApi'
import type {ILoginFormInput} from '../../../global/type'
import useCookie from '../../../hooks/useCookie'
import { SecondaryNavbar } from '../NavBar/NavBar'
import {SecondaryNavbar} from '../NavBar/NavBar'
import Toast from '../Toast/Toast'
const LogIn = () => {
const { apiUrl } = useApiUrlStore()
const { register, handleSubmit } = useForm<ILoginFormInput>()
const { register, handleSubmit, setValue } = useForm<ILoginFormInput>()
const { user, logout, ready, authenticated } = usePrivy()
const { signMessage } = useSignMessage()
const { setCookie } = useCookie()
// Prefill the name field with the Privy DID when user is available
useEffect(() => {
if (user?.id) {
console.log(user)
setValue('name', user.id)
}
}, [user?.id, setValue])
const onSubmit: SubmitHandler<ILoginFormInput> = async (form) => {
if (!authenticated || !user || !user.wallet?.address) {
const t = new Toast('Error: Not authenticated')

View File

@@ -92,7 +92,7 @@ const TradesModal: React.FC<TradesModalProps> = ({
) : strategyData ? (
<div>
<div className="mb-4">
<p className="font-bold">Strategy: {strategyData.strategyName}</p>
<p className="font-bold">Strategy: {strategyData.name}</p>
<p>Win Rate: {strategyData.winRate?.toFixed(2)}%</p>
<p>PnL: {strategyData.pnL?.toFixed(2)} $</p>
<p>Wins: {strategyData.wins} / Losses: {strategyData.losses}</p>
@@ -122,10 +122,10 @@ const TradesModal: React.FC<TradesModalProps> = ({
{position.originDirection}
</td>
<td>{position.status}</td>
<td>{position.open.price.toFixed(2)}</td>
<td>{position.open.quantity.toFixed(4)}</td>
<td className={position.profitAndLoss?.realized && position.profitAndLoss.realized > 0 ? 'text-success' : 'text-error'}>
{position.profitAndLoss?.realized?.toFixed(2) || '0.00'} $
<td>{position.Open.price.toFixed(2)}</td>
<td>{position.Open.quantity.toFixed(4)}</td>
<td className={position.ProfitAndLoss?.realized && position.ProfitAndLoss.realized > 0 ? 'text-success' : 'text-error'}>
{position.ProfitAndLoss?.realized?.toFixed(2) || '0.00'} $
</td>
<td>
{position.status !== 'Finished' && (

View File

@@ -17,8 +17,8 @@ import {
Timeframe,
TradingBotConfigRequest,
} from '../../../generated/ManagingApi'
import type {BacktestModalProps, IBacktestsFormInput,} from '../../../global/type'
import {Loader, Slider} from '../../atoms'
import type {BacktestModalProps, IBacktestsFormInput,} from '../../../global/type.tsx'
import {Loader} from '../../atoms'
import {Modal, Toast} from '../../mollecules'
import FormInput from '../../mollecules/FormInput/FormInput'
import CustomMoneyManagement from '../CustomMoneyManagement/CustomMoneyManagement'
@@ -169,7 +169,6 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
loopbackPeriod: customScenario.loopbackPeriod
} : undefined,
timeframe: form.timeframe,
botType: form.botType,
isForWatchingOnly: false, // Always false for backtests
cooldownPeriod: form.cooldownPeriod || 1,
maxLossStreak: form.maxLossStreak || 0,
@@ -181,7 +180,8 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
useSynthApi: form.useSynthApi ?? false,
useForPositionSizing: form.useForPositionSizing ?? true,
useForSignalFiltering: form.useForSignalFiltering ?? true,
useForDynamicStopLoss: form.useForDynamicStopLoss ?? true
useForDynamicStopLoss: form.useForDynamicStopLoss ?? true,
flipPosition: false
};
// Create the RunBacktestRequest
@@ -189,11 +189,7 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
config: tradingBotConfigRequest, // Use the request object
startDate: new Date(form.startDate),
endDate: new Date(form.endDate),
balance: form.balance,
watchOnly: false,
save: form.save || false,
moneyManagementName: customMoneyManagement ? undefined : selectedMoneyManagement,
moneyManagement: customMoneyManagement
save: form.save || false
};
const backtest = await backtestClient.backtest_Run(request);
@@ -578,29 +574,6 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
</FormInput>
</div>
{/* Loop Slider (if enabled) */}
{showLoopSlider && (
<FormInput
label={
<div className="flex items-center gap-2">
Loop
<div className="tooltip tooltip-top" data-tip="Number of optimization loops to run for money management. Each loop uses the optimized parameters from the previous iteration">
<span className="badge badge-info badge-xs">i</span>
</div>
</div>
}
htmlFor="loop"
>
<Slider
id="loopSlider"
min="1"
max="10"
value={selectedLoopQuantity.toString()}
onChange={(e) => setLoopQuantity(Number(e.target.value))}
></Slider>
</FormInput>
)}
{/* Max Loss Streak & Max Position Time */}
<div className="grid grid-cols-2 gap-4">
<FormInput

View File

@@ -6,6 +6,7 @@ import {
DataClient,
GetCandlesWithIndicatorsRequest,
IndicatorType,
Position,
SignalType
} from '../../../generated/ManagingApi'
import {CardPosition, CardText} from '../../mollecules'
@@ -92,18 +93,18 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
const config = currentBacktest.config;
// Helper function to calculate position open time in hours
const calculateOpenTimeInHours = (position: any) => {
const openDate = new Date(position.open.date);
const calculateOpenTimeInHours = (position: Position) => {
const openDate = new Date(position.Open.date);
let closeDate: Date | null = null;
// Determine close date based on realized P&L (matching backend logic)
if (position.profitAndLoss?.realized != null) {
if (position.profitAndLoss.realized > 0) {
if (position.ProfitAndLoss?.realized != null) {
if (position.ProfitAndLoss.realized > 0) {
// Profitable close = Take Profit
closeDate = new Date(position.takeProfit1.date);
closeDate = new Date(position.TakeProfit1.date);
} else {
// Loss or breakeven close = Stop Loss
closeDate = new Date(position.stopLoss.date);
closeDate = new Date(position.StopLoss.date);
}
}
@@ -117,7 +118,7 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
// Calculate average open time for winning positions
const getAverageOpenTimeWinning = () => {
const winningPositions = positions.filter((p) => {
const realized = p.profitAndLoss?.realized ?? 0;
const realized = p.ProfitAndLoss?.realized ?? 0;
return realized > 0;
});
@@ -134,7 +135,7 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
// Calculate average open time for losing positions
const getAverageOpenTimeLosing = () => {
const losingPositions = positions.filter((p) => {
const realized = p.profitAndLoss?.realized ?? 0;
const realized = p.ProfitAndLoss?.realized ?? 0;
return realized <= 0;
});
@@ -151,7 +152,7 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
// Calculate maximum open time for winning positions
const getMaxOpenTimeWinning = () => {
const winningPositions = positions.filter((p) => {
const realized = p.profitAndLoss?.realized ?? 0;
const realized = p.ProfitAndLoss?.realized ?? 0;
return realized > 0;
});
@@ -183,19 +184,19 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
positions.forEach((position) => {
// Calculate volume for open trade
const openLeverage = position.open.leverage || 1;
const openVolume = position.open.quantity * position.open.price * openLeverage;
const openLeverage = position.Open.leverage || 1;
const openVolume = position.Open.quantity * position.Open.price * openLeverage;
totalVolume += openVolume;
// Calculate volume for close trade (stopLoss or takeProfit based on realized P&L)
if (position.profitAndLoss?.realized != null) {
if (position.ProfitAndLoss?.realized != null) {
let closeTrade;
if (position.profitAndLoss.realized > 0) {
if (position.ProfitAndLoss.realized > 0) {
// Profitable close = Take Profit
closeTrade = position.takeProfit1;
closeTrade = position.TakeProfit1;
} else {
// Loss or breakeven close = Stop Loss
closeTrade = position.stopLoss;
closeTrade = position.StopLoss;
}
if (closeTrade) {
@@ -226,8 +227,8 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
const candleTimeframeMs = new Date(candles[1].date).getTime() - new Date(candles[0].date).getTime();
const sortedPositions = [...positions].sort((a, b) => {
const dateA = new Date(a.open.date).getTime();
const dateB = new Date(b.open.date).getTime();
const dateA = new Date(a.Open.date).getTime();
const dateB = new Date(b.Open.date).getTime();
return dateA - dateB;
});
@@ -237,23 +238,23 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
const currentPosition = sortedPositions[i];
const nextPosition = sortedPositions[i + 1];
const currentRealized = currentPosition.profitAndLoss?.realized ?? 0;
const nextRealized = nextPosition.profitAndLoss?.realized ?? 0;
const currentRealized = currentPosition.ProfitAndLoss?.realized ?? 0;
const nextRealized = nextPosition.ProfitAndLoss?.realized ?? 0;
// Check if current position is winning and next position is losing
if (currentRealized > 0 && nextRealized <= 0) {
// Calculate the close time of the current (winning) position
let currentCloseDate: Date | null = null;
if (currentPosition.profitAndLoss?.realized != null) {
if (currentPosition.profitAndLoss.realized > 0) {
currentCloseDate = new Date(currentPosition.takeProfit1.date);
if (currentPosition.ProfitAndLoss?.realized != null) {
if (currentPosition.ProfitAndLoss.realized > 0) {
currentCloseDate = new Date(currentPosition.TakeProfit1.date);
} else {
currentCloseDate = new Date(currentPosition.stopLoss.date);
currentCloseDate = new Date(currentPosition.StopLoss.date);
}
}
if (currentCloseDate) {
const nextOpenDate = new Date(nextPosition.open.date);
const nextOpenDate = new Date(nextPosition.Open.date);
const gapInMs = nextOpenDate.getTime() - currentCloseDate.getTime();
if (gapInMs >= 0) { // Only consider positive gaps
@@ -298,7 +299,7 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
if (positions.length === 0) return "0.00";
// Get all trade dates and sort them
const tradeDates = positions.map(position => new Date(position.open.date)).sort((a, b) => a.getTime() - b.getTime());
const tradeDates = positions.map(position => new Date(position.Open.date)).sort((a, b) => a.getTime() - b.getTime());
if (tradeDates.length < 2) return positions.length.toString();
@@ -327,14 +328,14 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
<CardPosition
positivePosition={true}
positions={positions.filter((p) => {
const realized = p.profitAndLoss?.realized ?? 0
const realized = p.ProfitAndLoss?.realized ?? 0
return realized > 0 ? p : null
})}
></CardPosition>
<CardPosition
positivePosition={false}
positions={positions.filter((p) => {
const realized = p.profitAndLoss?.realized ?? 0
const realized = p.ProfitAndLoss?.realized ?? 0
return realized <= 0 ? p : null
})}
></CardPosition>
@@ -371,12 +372,6 @@ const BacktestRowDetails: React.FC<IBacktestRowDetailsProps> = ({
(config.moneyManagement?.takeProfit * 100).toFixed(2) + "%" + " Lev.: x" + config.moneyManagement?.leverage
}
></CardText>
<CardText
title="Optimized Money Management"
content={
"SL: " + currentBacktest.optimizedMoneyManagement?.stopLoss.toFixed(2) + "% TP: " + currentBacktest.optimizedMoneyManagement?.takeProfit.toFixed(2) + "%"
}
></CardText>
<CardText
title="Avg Open Time (Winning)"
content={getAverageOpenTimeWinning() + " hours"}

View File

@@ -19,8 +19,8 @@ import type {
IndicatorsResultBase,
IndicatorType,
KeyValuePairOfDateTimeAndDecimal,
LightSignal,
Position,
Signal,
} from '../../../../generated/ManagingApi'
import {PositionStatus, TradeDirection,} from '../../../../generated/ManagingApi'
import useTheme from '../../../../hooks/useTheme'
@@ -43,7 +43,7 @@ import useTheme from '../../../../hooks/useTheme'
type ITradeChartProps = {
candles: Candle[]
positions: Position[]
signals: Signal[]
signals: LightSignal[]
walletBalances?: KeyValuePairOfDateTimeAndDecimal[] | null
indicatorsValues?: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; } | null;
stream?: Candle | null
@@ -66,7 +66,6 @@ const TradeChart = ({
const chart = useRef<IChartApi>()
const {themeProperty} = useTheme()
const theme = themeProperty()
console.log(theme)
const series1 = useRef<ISeriesApi<'Candlestick'>>()
const [timeDiff, setTimeDiff] = useState<number>(0)
const [candleCount, setCandleCount] = useState<number>(candles.length)
@@ -220,7 +219,7 @@ const TradeChart = ({
const negativeColor = theme.error
const positiveColor = theme.success
const status = position.status
const realized = position.profitAndLoss?.realized ?? 0
const realized = position.ProfitAndLoss?.realized ?? 0
if (status != undefined) {
if (
@@ -376,20 +375,19 @@ const TradeChart = ({
getPositionColor(p),
p.originDirection,
p.date,
p.open.price.toString()
p.Open?.price?.toString()
)
)
markers.push(...positionMarkers)
const lastPositionOpen = positions[positions.length - 1]
if (lastPositionOpen) {
series1.current.createPriceLine(
buildLine(theme.error, lastPositionOpen.stopLoss.price, 'SL')
buildLine(theme.error, lastPositionOpen.StopLoss?.price, 'SL')
)
series1.current.createPriceLine(
buildLine(theme.success, lastPositionOpen.takeProfit1.price, 'TP')
buildLine(theme.success, lastPositionOpen.TakeProfit1?.price, 'TP')
)
}
}

View File

@@ -2659,14 +2659,14 @@ export class SettingsClient extends AuthorizedApiBase {
this.baseUrl = baseUrl ?? "http://localhost:5000";
}
settings_SetupSettings(): Promise<FileResponse> {
settings_SetupSettings(): Promise<boolean> {
let url_ = this.baseUrl + "/Settings";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/octet-stream"
"Accept": "application/json"
}
};
@@ -2677,26 +2677,21 @@ export class SettingsClient extends AuthorizedApiBase {
});
}
protected processSettings_SetupSettings(response: Response): Promise<FileResponse> {
protected processSettings_SetupSettings(response: Response): Promise<boolean> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200 || status === 206) {
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
if (fileName) {
fileName = decodeURIComponent(fileName);
} else {
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
}
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
if (status === 200) {
return response.text().then((_responseText) => {
let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as boolean;
return result200;
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<FileResponse>(null as any);
return Promise.resolve<boolean>(null as any);
}
settings_ResetSettings(): Promise<boolean> {
@@ -3658,14 +3653,13 @@ export interface Backtest {
hodlPercentage: number;
config: TradingBotConfig;
positions: Position[];
signals: Signal[];
signals: LightSignal[];
candles: Candle[];
startDate: Date;
endDate: Date;
statistics: PerformanceMetrics;
fees: number;
walletBalances: KeyValuePairOfDateTimeAndDecimal[];
optimizedMoneyManagement: MoneyManagement;
user: User;
indicatorsValues: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; };
score: number;
@@ -3793,11 +3787,11 @@ export interface Position {
originDirection: TradeDirection;
ticker: Ticker;
moneyManagement: MoneyManagement;
open: Trade;
stopLoss: Trade;
takeProfit1: Trade;
takeProfit2?: Trade | null;
profitAndLoss?: ProfitAndLoss | null;
Open: Trade;
StopLoss: Trade;
TakeProfit1: Trade;
TakeProfit2?: Trade | null;
ProfitAndLoss?: ProfitAndLoss | null;
status: PositionStatus;
signalIdentifier?: string | null;
identifier: string;
@@ -3812,7 +3806,7 @@ export enum TradeDirection {
}
export interface Trade {
fee?: number;
fee: number;
date: Date;
direction: TradeDirection;
status: TradeStatus;
@@ -3820,9 +3814,9 @@ export interface Trade {
ticker: Ticker;
quantity: number;
price: number;
leverage?: number;
leverage: number;
exchangeOrderId: string;
message?: string | null;
message: string;
}
export enum TradeStatus {
@@ -3876,7 +3870,7 @@ export enum PositionInitiator {
export interface ValueObject {
}
export interface Signal extends ValueObject {
export interface LightSignal extends ValueObject {
status: SignalStatus;
direction: TradeDirection;
confidence: Confidence;
@@ -3888,7 +3882,6 @@ export interface Signal extends ValueObject {
exchange: TradingExchanges;
indicatorType: IndicatorType;
signalType: SignalType;
user?: User | null;
indicatorName: string;
}
@@ -3912,15 +3905,10 @@ export interface Candle {
date: Date;
open: number;
close: number;
volume?: number;
high: number;
low: number;
baseVolume?: number;
quoteVolume?: number;
tradeCount?: number;
takerBuyBaseVolume?: number;
takerBuyQuoteVolume?: number;
timeframe: Timeframe;
volume?: number;
}
export interface PerformanceMetrics {
@@ -4127,7 +4115,7 @@ export interface BundleBacktestRequest {
status: BundleBacktestRequestStatus;
name: string;
backtestRequestsJson: string;
results?: Backtest[] | null;
results?: string[] | null;
totalBacktests: number;
completedBacktests: number;
failedBacktests: number;
@@ -4251,7 +4239,7 @@ export enum BotType {
export interface TradingBotResponse {
status: string;
signals: Signal[];
signals: LightSignal[];
positions: Position[];
candles: Candle[];
winRate: number;
@@ -4299,11 +4287,11 @@ export interface Spotlight {
export interface TickerSignal {
ticker: Ticker;
fiveMinutes: Signal[];
fifteenMinutes: Signal[];
oneHour: Signal[];
fourHour: Signal[];
oneDay: Signal[];
fiveMinutes: LightSignal[];
fifteenMinutes: LightSignal[];
oneHour: LightSignal[];
fourHour: LightSignal[];
oneDay: LightSignal[];
}
export interface CandlesWithIndicatorsResponse {

View File

@@ -225,14 +225,13 @@ export interface Backtest {
hodlPercentage: number;
config: TradingBotConfig;
positions: Position[];
signals: Signal[];
signals: LightSignal[];
candles: Candle[];
startDate: Date;
endDate: Date;
statistics: PerformanceMetrics;
fees: number;
walletBalances: KeyValuePairOfDateTimeAndDecimal[];
optimizedMoneyManagement: MoneyManagement;
user: User;
indicatorsValues: { [key in keyof typeof IndicatorType]?: IndicatorsResultBase; };
score: number;
@@ -360,11 +359,11 @@ export interface Position {
originDirection: TradeDirection;
ticker: Ticker;
moneyManagement: MoneyManagement;
open: Trade;
stopLoss: Trade;
takeProfit1: Trade;
takeProfit2?: Trade | null;
profitAndLoss?: ProfitAndLoss | null;
Open: Trade;
StopLoss: Trade;
TakeProfit1: Trade;
TakeProfit2?: Trade | null;
ProfitAndLoss?: ProfitAndLoss | null;
status: PositionStatus;
signalIdentifier?: string | null;
identifier: string;
@@ -379,7 +378,7 @@ export enum TradeDirection {
}
export interface Trade {
fee?: number;
fee: number;
date: Date;
direction: TradeDirection;
status: TradeStatus;
@@ -387,9 +386,9 @@ export interface Trade {
ticker: Ticker;
quantity: number;
price: number;
leverage?: number;
leverage: number;
exchangeOrderId: string;
message?: string | null;
message: string;
}
export enum TradeStatus {
@@ -443,7 +442,7 @@ export enum PositionInitiator {
export interface ValueObject {
}
export interface Signal extends ValueObject {
export interface LightSignal extends ValueObject {
status: SignalStatus;
direction: TradeDirection;
confidence: Confidence;
@@ -455,7 +454,6 @@ export interface Signal extends ValueObject {
exchange: TradingExchanges;
indicatorType: IndicatorType;
signalType: SignalType;
user?: User | null;
indicatorName: string;
}
@@ -479,15 +477,10 @@ export interface Candle {
date: Date;
open: number;
close: number;
volume?: number;
high: number;
low: number;
baseVolume?: number;
quoteVolume?: number;
tradeCount?: number;
takerBuyBaseVolume?: number;
takerBuyQuoteVolume?: number;
timeframe: Timeframe;
volume?: number;
}
export interface PerformanceMetrics {
@@ -694,7 +687,7 @@ export interface BundleBacktestRequest {
status: BundleBacktestRequestStatus;
name: string;
backtestRequestsJson: string;
results?: Backtest[] | null;
results?: string[] | null;
totalBacktests: number;
completedBacktests: number;
failedBacktests: number;
@@ -818,7 +811,7 @@ export enum BotType {
export interface TradingBotResponse {
status: string;
signals: Signal[];
signals: LightSignal[];
positions: Position[];
candles: Candle[];
winRate: number;
@@ -866,11 +859,11 @@ export interface Spotlight {
export interface TickerSignal {
ticker: Ticker;
fiveMinutes: Signal[];
fifteenMinutes: Signal[];
oneHour: Signal[];
fourHour: Signal[];
oneDay: Signal[];
fiveMinutes: LightSignal[];
fifteenMinutes: LightSignal[];
oneHour: LightSignal[];
fourHour: LightSignal[];
oneDay: LightSignal[];
}
export interface CandlesWithIndicatorsResponse {

View File

@@ -3,11 +3,9 @@ import React, {useState} from 'react'
import 'react-toastify/dist/ReactToastify.css'
import {Tabs} from '../../components/mollecules'
import BacktestScanner from './backtestScanner'
import BacktestUpload from './backtestUpload'
import BacktestGenetic from './backtestGenetic'
import BacktestGeneticBundle from './backtestGeneticBundle'
import BacktestBundleForm from './backtestBundleForm';
import type {TabsType} from '../../global/type.tsx'
import BacktestBundleForm from './backtestBundleForm'
// Tabs Array
const tabs: TabsType = [
@@ -21,19 +19,9 @@ const tabs: TabsType = [
index: 1,
label: 'Scanner',
},
{
Component: BacktestUpload,
index: 2,
label: 'Upload',
},
{
Component: BacktestGenetic,
index: 3,
label: 'Genetic',
},
{
Component: BacktestGeneticBundle,
index: 4,
index: 2,
label: 'GeneticBundle',
},
]

View File

@@ -1,5 +1,5 @@
import React, {useState} from 'react';
import {BacktestClient} from '../../generated/ManagingApi';
import {AccountClient, BacktestClient} from '../../generated/ManagingApi';
import type {
MoneyManagementRequest,
RunBacktestRequest,
@@ -12,6 +12,7 @@ import {useCustomScenario} from '../../app/store/customScenario';
import useApiUrlStore from '../../app/store/apiStore';
import Toast from '../../components/mollecules/Toast/Toast';
import BundleRequestsTable from './bundleRequestsTable';
import {useQuery} from '@tanstack/react-query';
// Placeholder types (replace with your actual types)
type Indicator = { name: string; params?: Record<string, any> };
@@ -52,6 +53,22 @@ const timeframeMap: Record<string, Timeframe> = {
const BacktestBundleForm: React.FC = () => {
const {apiUrl} = useApiUrlStore()
// API clients
const accountClient = new AccountClient({}, apiUrl);
// Data fetching
const { data: accounts, isSuccess } = useQuery({
queryFn: async () => {
const fetchedAccounts = await accountClient.account_GetAccounts();
if (fetchedAccounts.length > 0 && accountName === 'default') {
setAccountName(fetchedAccounts[0].name);
}
return fetchedAccounts;
},
queryKey: ['accounts'],
});
// Form state
const [strategyName, setStrategyName] = useState('');
const [loopback, setLoopback] = useState(14);
@@ -71,6 +88,7 @@ const BacktestBundleForm: React.FC = () => {
const [flipOnlyInProfit, setFlipOnlyInProfit] = useState(false);
const [closeEarly, setCloseEarly] = useState(false);
const [startingCapital, setStartingCapital] = useState(10000);
const [accountName, setAccountName] = useState(accounts?.[0]?.name ?? '');
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState<string | null>(null);
@@ -99,7 +117,7 @@ const BacktestBundleForm: React.FC = () => {
timeframe: timeframeMap[timeframe],
};
const config: TradingBotConfigRequest = {
accountName: 'default', // TODO: let user pick
accountName: accountName,
ticker: tickerMap[asset],
timeframe: timeframeMap[timeframe],
isForWatchingOnly: false,
@@ -170,6 +188,22 @@ const BacktestBundleForm: React.FC = () => {
/>
</div>
{/* Select account */}
<div className="mb-4">
<label className="label">Select account</label>
<select
className="select select-bordered w-full"
value={accountName}
onChange={e => setAccountName(e.target.value)}
>
{accounts?.map(account => (
<option key={account.name} value={account.name}>
{account.name}
</option>
))}
</select>
</div>
{/* Scenario/Indicators section */}
<div className="mb-4">
<CustomScenario
@@ -431,4 +465,4 @@ const BacktestBundleForm: React.FC = () => {
);
};
export default BacktestBundleForm;
export default BacktestBundleForm;

View File

@@ -5,21 +5,22 @@ import {useForm} from 'react-hook-form'
import useApiUrlStore from '../../app/store/apiStore'
import useBacktestStore from '../../app/store/backtestStore'
import {
AccountClient,
type Backtest,
BacktestClient,
DataClient,
type IndicatorRequest,
IndicatorType,
MoneyManagementClient,
type MoneyManagementRequest,
type RunBacktestRequest,
ScenarioClient,
type ScenarioRequest,
SignalType,
Ticker,
Timeframe,
type TradingBotConfigRequest,
AccountClient,
type Backtest,
BacktestClient,
DataClient,
type IndicatorRequest,
IndicatorType,
LightBacktestResponse,
MoneyManagementClient,
type MoneyManagementRequest,
type RunBacktestRequest,
ScenarioClient,
type ScenarioRequest,
SignalType,
Ticker,
Timeframe,
type TradingBotConfigRequest,
} from '../../generated/ManagingApi'
import {Toast} from '../../components/mollecules'
import BacktestTable from '../../components/organism/Backtest/backtestTable'
@@ -2057,7 +2058,7 @@ const BacktestGenetic: React.FC = () => {
<div className="card bg-base-100 shadow-xl">
<div className="card-body">
<h3 className="card-title">Best Results</h3>
<BacktestTable list={results.map(r => r.backtest).filter(Boolean) as Backtest[]} displaySummary={false} />
<BacktestTable list={results.map(r => r.backtest).filter(Boolean) as LightBacktestResponse[]} displaySummary={false} />
</div>
</div>
</div>

View File

@@ -7,12 +7,12 @@ import ManualPositionModal from '../../components/mollecules/ManualPositionModal
import TradesModal from '../../components/mollecules/TradesModal/TradesModal'
import {TradeChart, UnifiedTradingModal} from '../../components/organism'
import {
BotClient,
BotType,
MoneyManagement,
Position,
TradingBotResponse,
UserClient
BotClient,
BotType,
MoneyManagement,
Position,
TradingBotResponse,
UserClient
} from '../../generated/ManagingApi'
import type {IBotList} from '../../global/type.tsx'
import MoneyManagementModal from '../settingsPage/moneymanagement/moneyManagementModal'
@@ -300,14 +300,14 @@ const BotList: React.FC<IBotList> = ({ list }) => {
<CardPosition
positivePosition={true}
positions={bot.positions.filter((p: Position) => {
const realized = p.profitAndLoss?.realized ?? 0
const realized = p.ProfitAndLoss?.realized ?? 0
return realized > 0 ? p : null
})}
></CardPosition>
<CardPosition
positivePosition={false}
positions={bot.positions.filter((p: Position) => {
const realized = p.profitAndLoss?.realized ?? 0
const realized = p.ProfitAndLoss?.realized ?? 0
return realized <= 0 ? p : null
})}
></CardPosition>