Fixes for bots running (#22)
* Fixes for bots running * Up botmanager * Add cooldown * Refact can open position * Add cooldown Period and MaxLossStreak * Add agentName * Add env variable for botManager * Always enable Botmanager * Fix bot handle * Fix get positions * Add Ticker url * Dont start stopped bot * fix
This commit is contained in:
@@ -7,13 +7,15 @@ import Toast from '../Toast/Toast'
|
||||
|
||||
interface TradesModalProps {
|
||||
showModal: boolean
|
||||
botName: string | null
|
||||
agentName: string | null
|
||||
strategyName: string | null
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const TradesModal: React.FC<TradesModalProps> = ({
|
||||
showModal,
|
||||
botName,
|
||||
strategyName,
|
||||
agentName,
|
||||
onClose,
|
||||
}) => {
|
||||
const { apiUrl } = useApiUrlStore()
|
||||
@@ -22,19 +24,19 @@ const TradesModal: React.FC<TradesModalProps> = ({
|
||||
const [closingPosition, setClosingPosition] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (showModal && botName) {
|
||||
if (showModal && strategyName && agentName) {
|
||||
fetchStrategyData()
|
||||
}
|
||||
}, [showModal, botName])
|
||||
}, [showModal, strategyName, agentName])
|
||||
|
||||
const fetchStrategyData = async () => {
|
||||
if (!botName) return
|
||||
if (!strategyName || !agentName) return
|
||||
|
||||
setLoading(true)
|
||||
const client = new DataClient({}, apiUrl)
|
||||
|
||||
try {
|
||||
const data = await client.data_GetUserStrategy("Oda", botName)
|
||||
const data = await client.data_GetUserStrategy(agentName, strategyName)
|
||||
setStrategyData(data)
|
||||
} catch (error) {
|
||||
console.error('Error fetching strategy data:', error)
|
||||
@@ -46,7 +48,7 @@ const TradesModal: React.FC<TradesModalProps> = ({
|
||||
}
|
||||
|
||||
const closePosition = async (position: Position) => {
|
||||
if (!botName) return
|
||||
if (!agentName) return
|
||||
|
||||
try {
|
||||
setClosingPosition(position.identifier)
|
||||
@@ -56,7 +58,7 @@ const TradesModal: React.FC<TradesModalProps> = ({
|
||||
// Use BotClient instead of fetch
|
||||
const botClient = new BotClient({}, apiUrl)
|
||||
const request: ClosePositionRequest = {
|
||||
botName: botName,
|
||||
identifier: agentName,
|
||||
positionId: position.identifier
|
||||
}
|
||||
|
||||
@@ -78,7 +80,7 @@ const TradesModal: React.FC<TradesModalProps> = ({
|
||||
<Modal
|
||||
showModal={showModal}
|
||||
onClose={onClose}
|
||||
titleHeader={`Trades for ${botName}`}
|
||||
titleHeader={`Trades for ${strategyName}`}
|
||||
>
|
||||
<div className="mt-4">
|
||||
{loading ? (
|
||||
|
||||
@@ -42,7 +42,9 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
|
||||
const { register, handleSubmit, setValue } = useForm<IBacktestsFormInput>({
|
||||
defaultValues: {
|
||||
startDate: defaultStartDateString,
|
||||
endDate: defaultEndDateString
|
||||
endDate: defaultEndDateString,
|
||||
cooldownPeriod: 1, // Default cooldown period of 1 minute
|
||||
maxLossStreak: 0 // Default max loss streak of 0 (no limit)
|
||||
}
|
||||
});
|
||||
const [selectedAccount, setSelectedAccount] = useState<string>('')
|
||||
@@ -110,6 +112,8 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
|
||||
new Date(form.startDate), // startDate
|
||||
new Date(form.endDate), // endDate
|
||||
form.save,
|
||||
form.cooldownPeriod, // Use the cooldown period from the form
|
||||
form.maxLossStreak, // Add the max loss streak parameter
|
||||
customMoneyManagement
|
||||
)
|
||||
.then((backtest: Backtest) => {
|
||||
@@ -362,6 +366,28 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
|
||||
/>
|
||||
</FormInput>
|
||||
|
||||
<FormInput label="Cooldown Period (candles)" htmlFor="cooldownPeriod">
|
||||
<input
|
||||
type="number"
|
||||
className="input input-bordered w-full"
|
||||
min="1"
|
||||
step="1"
|
||||
{...register('cooldownPeriod', { valueAsNumber: true })}
|
||||
/>
|
||||
</FormInput>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<FormInput label="Max Loss Streak" htmlFor="maxLossStreak">
|
||||
<input
|
||||
type="number"
|
||||
className="input input-bordered w-full"
|
||||
min="0"
|
||||
step="1"
|
||||
{...register('maxLossStreak', { valueAsNumber: true })}
|
||||
/>
|
||||
</FormInput>
|
||||
|
||||
<FormInput label="Save" htmlFor="save">
|
||||
<input
|
||||
type="checkbox"
|
||||
|
||||
@@ -337,7 +337,7 @@ export class BacktestClient extends AuthorizedApiBase {
|
||||
return Promise.resolve<Backtest>(null as any);
|
||||
}
|
||||
|
||||
backtest_Run(accountName: string | null | undefined, botType: BotType | undefined, ticker: Ticker | undefined, scenarioName: string | null | undefined, timeframe: Timeframe | undefined, watchOnly: boolean | undefined, balance: number | undefined, moneyManagementName: string | null | undefined, startDate: Date | undefined, endDate: Date | undefined, save: boolean | undefined, moneyManagement: MoneyManagement | undefined): Promise<Backtest> {
|
||||
backtest_Run(accountName: string | null | undefined, botType: BotType | undefined, ticker: Ticker | undefined, scenarioName: string | null | undefined, timeframe: Timeframe | undefined, watchOnly: boolean | undefined, balance: number | undefined, moneyManagementName: string | null | undefined, startDate: Date | undefined, endDate: Date | undefined, save: boolean | undefined, cooldownPeriod: number | undefined, maxLossStreak: number | undefined, moneyManagement: MoneyManagement | undefined): Promise<Backtest> {
|
||||
let url_ = this.baseUrl + "/Backtest/Run?";
|
||||
if (accountName !== undefined && accountName !== null)
|
||||
url_ += "accountName=" + encodeURIComponent("" + accountName) + "&";
|
||||
@@ -377,6 +377,14 @@ export class BacktestClient extends AuthorizedApiBase {
|
||||
throw new Error("The parameter 'save' cannot be null.");
|
||||
else if (save !== undefined)
|
||||
url_ += "save=" + encodeURIComponent("" + save) + "&";
|
||||
if (cooldownPeriod === null)
|
||||
throw new Error("The parameter 'cooldownPeriod' cannot be null.");
|
||||
else if (cooldownPeriod !== undefined)
|
||||
url_ += "cooldownPeriod=" + encodeURIComponent("" + cooldownPeriod) + "&";
|
||||
if (maxLossStreak === null)
|
||||
throw new Error("The parameter 'maxLossStreak' cannot be null.");
|
||||
else if (maxLossStreak !== undefined)
|
||||
url_ += "maxLossStreak=" + encodeURIComponent("" + maxLossStreak) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
const content_ = JSON.stringify(moneyManagement);
|
||||
@@ -465,14 +473,14 @@ export class BotClient extends AuthorizedApiBase {
|
||||
return Promise.resolve<string>(null as any);
|
||||
}
|
||||
|
||||
bot_Stop(botType: BotType | undefined, botName: string | null | undefined): Promise<string> {
|
||||
bot_Stop(botType: BotType | undefined, identifier: string | null | undefined): Promise<string> {
|
||||
let url_ = this.baseUrl + "/Bot/Stop?";
|
||||
if (botType === null)
|
||||
throw new Error("The parameter 'botType' cannot be null.");
|
||||
else if (botType !== undefined)
|
||||
url_ += "botType=" + encodeURIComponent("" + botType) + "&";
|
||||
if (botName !== undefined && botName !== null)
|
||||
url_ += "botName=" + encodeURIComponent("" + botName) + "&";
|
||||
if (identifier !== undefined && identifier !== null)
|
||||
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
@@ -506,10 +514,10 @@ export class BotClient extends AuthorizedApiBase {
|
||||
return Promise.resolve<string>(null as any);
|
||||
}
|
||||
|
||||
bot_Delete(botName: string | null | undefined): Promise<boolean> {
|
||||
bot_Delete(identifier: string | null | undefined): Promise<boolean> {
|
||||
let url_ = this.baseUrl + "/Bot/Delete?";
|
||||
if (botName !== undefined && botName !== null)
|
||||
url_ += "botName=" + encodeURIComponent("" + botName) + "&";
|
||||
if (identifier !== undefined && identifier !== null)
|
||||
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
@@ -578,14 +586,14 @@ export class BotClient extends AuthorizedApiBase {
|
||||
return Promise.resolve<string>(null as any);
|
||||
}
|
||||
|
||||
bot_Restart(botType: BotType | undefined, botName: string | null | undefined): Promise<string> {
|
||||
bot_Restart(botType: BotType | undefined, identifier: string | null | undefined): Promise<string> {
|
||||
let url_ = this.baseUrl + "/Bot/Restart?";
|
||||
if (botType === null)
|
||||
throw new Error("The parameter 'botType' cannot be null.");
|
||||
else if (botType !== undefined)
|
||||
url_ += "botType=" + encodeURIComponent("" + botType) + "&";
|
||||
if (botName !== undefined && botName !== null)
|
||||
url_ += "botName=" + encodeURIComponent("" + botName) + "&";
|
||||
if (identifier !== undefined && identifier !== null)
|
||||
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
@@ -654,10 +662,10 @@ export class BotClient extends AuthorizedApiBase {
|
||||
return Promise.resolve<string>(null as any);
|
||||
}
|
||||
|
||||
bot_ToggleIsForWatching(botName: string | null | undefined): Promise<string> {
|
||||
bot_ToggleIsForWatching(identifier: string | null | undefined): Promise<string> {
|
||||
let url_ = this.baseUrl + "/Bot/ToggleIsForWatching?";
|
||||
if (botName !== undefined && botName !== null)
|
||||
url_ += "botName=" + encodeURIComponent("" + botName) + "&";
|
||||
if (identifier !== undefined && identifier !== null)
|
||||
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
@@ -816,7 +824,7 @@ export class DataClient extends AuthorizedApiBase {
|
||||
this.baseUrl = baseUrl ?? "http://localhost:5000";
|
||||
}
|
||||
|
||||
data_GetTickers(timeframe: Timeframe | undefined): Promise<Ticker[]> {
|
||||
data_GetTickers(timeframe: Timeframe | undefined): Promise<TickerInfos[]> {
|
||||
let url_ = this.baseUrl + "/Data/GetTickers?";
|
||||
if (timeframe === null)
|
||||
throw new Error("The parameter 'timeframe' cannot be null.");
|
||||
@@ -838,13 +846,13 @@ export class DataClient extends AuthorizedApiBase {
|
||||
});
|
||||
}
|
||||
|
||||
protected processData_GetTickers(response: Response): Promise<Ticker[]> {
|
||||
protected processData_GetTickers(response: Response): Promise<TickerInfos[]> {
|
||||
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) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Ticker[];
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as TickerInfos[];
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
@@ -852,7 +860,7 @@ export class DataClient extends AuthorizedApiBase {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<Ticker[]>(null as any);
|
||||
return Promise.resolve<TickerInfos[]>(null as any);
|
||||
}
|
||||
|
||||
data_GetSpotlight(): Promise<SpotlightOverview> {
|
||||
@@ -2202,6 +2210,80 @@ export class UserClient extends AuthorizedApiBase {
|
||||
}
|
||||
return Promise.resolve<string>(null as any);
|
||||
}
|
||||
|
||||
user_GetCurrentUser(): Promise<User> {
|
||||
let url_ = this.baseUrl + "/User";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||
return this.http.fetch(url_, transformedOptions_);
|
||||
}).then((_response: Response) => {
|
||||
return this.processUser_GetCurrentUser(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processUser_GetCurrentUser(response: Response): Promise<User> {
|
||||
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) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as User;
|
||||
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<User>(null as any);
|
||||
}
|
||||
|
||||
user_UpdateAgentName(agentName: string): Promise<User> {
|
||||
let url_ = this.baseUrl + "/User/agent-name";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
const content_ = JSON.stringify(agentName);
|
||||
|
||||
let options_: RequestInit = {
|
||||
body: content_,
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||
return this.http.fetch(url_, transformedOptions_);
|
||||
}).then((_response: Response) => {
|
||||
return this.processUser_UpdateAgentName(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processUser_UpdateAgentName(response: Response): Promise<User> {
|
||||
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) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as User;
|
||||
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<User>(null as any);
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkflowClient extends AuthorizedApiBase {
|
||||
@@ -2397,6 +2479,7 @@ export enum AccountType {
|
||||
export interface User {
|
||||
name?: string | null;
|
||||
accounts?: Account[] | null;
|
||||
agentName?: string | null;
|
||||
}
|
||||
|
||||
export interface Balance {
|
||||
@@ -2839,15 +2922,15 @@ export interface SuperTrendResult extends ResultBase {
|
||||
}
|
||||
|
||||
export interface StartBotRequest {
|
||||
botType: BotType;
|
||||
botName: string;
|
||||
ticker: Ticker;
|
||||
timeframe: Timeframe;
|
||||
isForWatchOnly: boolean;
|
||||
scenario: string;
|
||||
accountName: string;
|
||||
moneyManagementName: string;
|
||||
initialTradingBalance: number;
|
||||
botType?: BotType;
|
||||
identifier?: string | null;
|
||||
ticker?: Ticker;
|
||||
scenario?: string | null;
|
||||
timeframe?: Timeframe;
|
||||
accountName?: string | null;
|
||||
moneyManagementName?: string | null;
|
||||
isForWatchOnly?: boolean;
|
||||
initialTradingBalance?: number;
|
||||
}
|
||||
|
||||
export interface TradingBot {
|
||||
@@ -2865,18 +2948,25 @@ export interface TradingBot {
|
||||
botType: BotType;
|
||||
accountName: string;
|
||||
moneyManagement: MoneyManagement;
|
||||
identifier: string;
|
||||
agentName: string;
|
||||
}
|
||||
|
||||
export interface OpenPositionManuallyRequest {
|
||||
botName?: string | null;
|
||||
identifier?: string | null;
|
||||
direction?: TradeDirection;
|
||||
}
|
||||
|
||||
export interface ClosePositionRequest {
|
||||
botName?: string | null;
|
||||
identifier?: string | null;
|
||||
positionId?: string | null;
|
||||
}
|
||||
|
||||
export interface TickerInfos {
|
||||
ticker?: Ticker;
|
||||
imageUrl?: string | null;
|
||||
}
|
||||
|
||||
export interface SpotlightOverview {
|
||||
spotlights: Spotlight[];
|
||||
dateTime: Date;
|
||||
@@ -2962,7 +3052,7 @@ export interface PlatformSummaryViewModel {
|
||||
}
|
||||
|
||||
export interface AgentSummaryViewModel {
|
||||
username?: string | null;
|
||||
agentName?: string | null;
|
||||
totalPnL?: number;
|
||||
pnLLast24h?: number;
|
||||
totalROI?: number;
|
||||
|
||||
@@ -111,6 +111,8 @@ export type IBacktestsFormInput = {
|
||||
loop: number
|
||||
startDate: string
|
||||
endDate: string
|
||||
cooldownPeriod: number
|
||||
maxLossStreak: number
|
||||
}
|
||||
|
||||
export type IBacktestCards = {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState } from 'react'
|
||||
import React, {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 BacktestLoop from './backtestLoop'
|
||||
import BacktestPlayground from './backtestPlayground'
|
||||
@@ -12,14 +12,14 @@ import BacktestUpload from './backtestUpload'
|
||||
// Tabs Array
|
||||
const tabs: TabsType = [
|
||||
{
|
||||
Component: BacktestScanner,
|
||||
Component: BacktestPlayground,
|
||||
index: 1,
|
||||
label: 'Scanner',
|
||||
label: 'Playground',
|
||||
},
|
||||
{
|
||||
Component: BacktestPlayground,
|
||||
Component: BacktestScanner,
|
||||
index: 2,
|
||||
label: 'Playground',
|
||||
label: 'Scanner',
|
||||
},
|
||||
{
|
||||
Component: BacktestLoop,
|
||||
|
||||
@@ -37,13 +37,13 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
const [showManualPositionModal, setShowManualPositionModal] = useState(false)
|
||||
const [selectedBotForManualPosition, setSelectedBotForManualPosition] = useState<string | null>(null)
|
||||
const [showTradesModal, setShowTradesModal] = useState(false)
|
||||
const [selectedBotForTrades, setSelectedBotForTrades] = useState<string | null>(null)
|
||||
const [selectedBotForTrades, setSelectedBotForTrades] = useState<{ identifier: string; agentName: string } | null>(null)
|
||||
|
||||
function getIsForWatchingBadge(isForWatchingOnly: boolean, name: string) {
|
||||
function getIsForWatchingBadge(isForWatchingOnly: boolean, identifier: string) {
|
||||
const classes =
|
||||
baseBadgeClass() + (isForWatchingOnly ? ' bg-accent' : ' bg-primary')
|
||||
return (
|
||||
<button className={classes} onClick={() => toggleIsForWatchingOnly(name)}>
|
||||
<button className={classes} onClick={() => toggleIsForWatchingOnly(identifier)}>
|
||||
{isForWatchingOnly ? (
|
||||
<p className="text-accent-content flex">
|
||||
<EyeIcon width={12}></EyeIcon>
|
||||
@@ -59,16 +59,16 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
)
|
||||
}
|
||||
|
||||
function toggleIsForWatchingOnly(name: string) {
|
||||
function toggleIsForWatchingOnly(identifier: string) {
|
||||
const t = new Toast('Switch watch only')
|
||||
client.bot_ToggleIsForWatching(name).then(() => {
|
||||
client.bot_ToggleIsForWatching(identifier).then(() => {
|
||||
t.update('success', 'Bot updated')
|
||||
})
|
||||
}
|
||||
function getDeleteBadge(name: string) {
|
||||
function getDeleteBadge(identifier: string) {
|
||||
const classes = baseBadgeClass() + 'bg-error'
|
||||
return (
|
||||
<button className={classes} onClick={() => deleteBot(name)}>
|
||||
<button className={classes} onClick={() => deleteBot(identifier)}>
|
||||
<p className="text-primary-content flex">
|
||||
<TrashIcon width={15}></TrashIcon>
|
||||
</p>
|
||||
@@ -77,7 +77,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
}
|
||||
function getToggleBotStatusBadge(
|
||||
status: string,
|
||||
name: string,
|
||||
identifier: string,
|
||||
botType: BotType
|
||||
) {
|
||||
const classes =
|
||||
@@ -85,7 +85,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
return (
|
||||
<button
|
||||
className={classes}
|
||||
onClick={() => toggleBotStatus(status, name, botType)}
|
||||
onClick={() => toggleBotStatus(status, identifier, botType)}
|
||||
>
|
||||
{status == 'Up' ? (
|
||||
<p className="text-accent-content flex">
|
||||
@@ -118,10 +118,10 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
)
|
||||
}
|
||||
|
||||
function getManualPositionBadge(botName: string) {
|
||||
function getManualPositionBadge(botIdentifier: string) {
|
||||
const classes = baseBadgeClass() + ' bg-info'
|
||||
return (
|
||||
<button className={classes} onClick={() => openManualPositionModal(botName)}>
|
||||
<button className={classes} onClick={() => openManualPositionModal(botIdentifier)}>
|
||||
<p className="text-primary-content flex">
|
||||
<PlusCircleIcon width={15}></PlusCircleIcon>
|
||||
</p>
|
||||
@@ -129,10 +129,10 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
)
|
||||
}
|
||||
|
||||
function getTradesBadge(botName: string) {
|
||||
function getTradesBadge(botIdentifier: string, agentName: string) {
|
||||
const classes = baseBadgeClass() + ' bg-secondary'
|
||||
return (
|
||||
<button className={classes} onClick={() => openTradesModal(botName)}>
|
||||
<button className={classes} onClick={() => openTradesModal(botIdentifier, agentName)}>
|
||||
<p className="text-primary-content flex">
|
||||
<ChartBarIcon width={15}></ChartBarIcon>
|
||||
</p>
|
||||
@@ -140,23 +140,23 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
)
|
||||
}
|
||||
|
||||
function openManualPositionModal(botName: string) {
|
||||
setSelectedBotForManualPosition(botName)
|
||||
function openManualPositionModal(botIdentifier: string) {
|
||||
setSelectedBotForManualPosition(botIdentifier)
|
||||
setShowManualPositionModal(true)
|
||||
}
|
||||
|
||||
function openTradesModal(botName: string) {
|
||||
setSelectedBotForTrades(botName)
|
||||
function openTradesModal(botIdentifier: string, agentName: string) {
|
||||
setSelectedBotForTrades({ identifier: botIdentifier, agentName })
|
||||
setShowTradesModal(true)
|
||||
}
|
||||
|
||||
function toggleBotStatus(status: string, name: string, botType: BotType) {
|
||||
function toggleBotStatus(status: string, identifier: string, botType: BotType) {
|
||||
const isUp = status == 'Up'
|
||||
const t = new Toast(isUp ? 'Stoping bot' : 'Restarting bot')
|
||||
|
||||
if (status == 'Up') {
|
||||
client
|
||||
.bot_Stop(botType, name)
|
||||
.bot_Stop(botType, identifier)
|
||||
.then(() => {
|
||||
t.update('success', 'Bot stopped')
|
||||
})
|
||||
@@ -165,7 +165,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
})
|
||||
} else if (status == 'Down') {
|
||||
client
|
||||
.bot_Restart(botType, name)
|
||||
.bot_Restart(botType, identifier)
|
||||
.then(() => {
|
||||
t.update('success', 'Bot up')
|
||||
})
|
||||
@@ -175,11 +175,11 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
}
|
||||
}
|
||||
|
||||
function deleteBot(name: string) {
|
||||
function deleteBot(identifier: string) {
|
||||
const t = new Toast('Deleting bot')
|
||||
|
||||
client
|
||||
.bot_Delete(name)
|
||||
.bot_Delete(identifier)
|
||||
.then(() => {
|
||||
t.update('success', 'Bot deleted')
|
||||
})
|
||||
@@ -211,10 +211,10 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
<h2 className="card-title text-sm">
|
||||
{bot.ticker}
|
||||
{getMoneyManagementBadge(bot.moneyManagement)}
|
||||
{getIsForWatchingBadge(bot.isForWatchingOnly, bot.name)}
|
||||
{getToggleBotStatusBadge(bot.status, bot.name, bot.botType)}
|
||||
{getManualPositionBadge(bot.name)}
|
||||
{getDeleteBadge(bot.name)}
|
||||
{getIsForWatchingBadge(bot.isForWatchingOnly, bot.identifier)}
|
||||
{getToggleBotStatusBadge(bot.status, bot.identifier, bot.botType)}
|
||||
{getManualPositionBadge(bot.identifier)}
|
||||
{getDeleteBadge(bot.identifier)}
|
||||
</h2>
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
<div className={baseBadgeClass(true)}>
|
||||
PNL {bot.profitAndLoss.toFixed(2).toString()} $
|
||||
</div>
|
||||
{getTradesBadge(bot.name)}
|
||||
{getTradesBadge(bot.identifier, bot.agentName)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -280,7 +280,8 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
/>
|
||||
<TradesModal
|
||||
showModal={showTradesModal}
|
||||
botName={selectedBotForTrades}
|
||||
strategyName={selectedBotForTrades?.identifier ?? null}
|
||||
agentName={selectedBotForTrades?.agentName ?? null}
|
||||
onClose={() => {
|
||||
setShowTradesModal(false)
|
||||
setSelectedBotForTrades(null)
|
||||
|
||||
101
src/Managing.WebApp/src/pages/settingsPage/UserInfoSettings.tsx
Normal file
101
src/Managing.WebApp/src/pages/settingsPage/UserInfoSettings.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import {useState} from 'react'
|
||||
import {useForm} from 'react-hook-form'
|
||||
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
import {UserClient} from '../../generated/ManagingApi'
|
||||
import Modal from '../../components/mollecules/Modal/Modal'
|
||||
import useApiUrlStore from '../../app/store/apiStore'
|
||||
import {Toast} from '../../components/mollecules'
|
||||
|
||||
type UpdateAgentNameForm = {
|
||||
agentName: string
|
||||
}
|
||||
|
||||
function UserInfoSettings() {
|
||||
const [showUpdateModal, setShowUpdateModal] = useState(false)
|
||||
const queryClient = useQueryClient()
|
||||
const { apiUrl } = useApiUrlStore()
|
||||
const api = new UserClient({}, apiUrl)
|
||||
|
||||
const { data: user } = useQuery({
|
||||
queryKey: ['user'],
|
||||
queryFn: () => api.user_GetCurrentUser(),
|
||||
})
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm<UpdateAgentNameForm>()
|
||||
|
||||
const onSubmit = async (data: UpdateAgentNameForm) => {
|
||||
const toast = new Toast('Updating agent name')
|
||||
try {
|
||||
await api.user_UpdateAgentName(data.agentName)
|
||||
queryClient.invalidateQueries({ queryKey: ['user'] })
|
||||
setShowUpdateModal(false)
|
||||
toast.update('success', 'Agent name updated successfully')
|
||||
} catch (error) {
|
||||
console.error('Error updating agent name:', error)
|
||||
toast.update('error', 'Failed to update agent name')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4">
|
||||
<div className="bg-base-200 rounded-lg p-6 shadow-lg">
|
||||
<h2 className="text-2xl font-bold mb-4">User Information</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="font-semibold">Name:</label>
|
||||
<p>{user?.name}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="font-semibold">Agent Name:</label>
|
||||
<p>{user?.agentName || 'Not set'}</p>
|
||||
<button
|
||||
className="btn btn-primary mt-2"
|
||||
onClick={() => setShowUpdateModal(true)}
|
||||
>
|
||||
Update Agent Name
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
showModal={showUpdateModal}
|
||||
onClose={() => setShowUpdateModal(false)}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
titleHeader="Update Agent Name"
|
||||
>
|
||||
<div className="form-control w-full">
|
||||
<label className="label">
|
||||
<span className="label-text">Agent Name</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="input input-bordered w-full"
|
||||
{...register('agentName', { required: 'Agent name is required' })}
|
||||
defaultValue={user?.agentName || ''}
|
||||
/>
|
||||
{errors.agentName && (
|
||||
<label className="label">
|
||||
<span className="label-text-alt text-error">
|
||||
{errors.agentName.message}
|
||||
</span>
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
<div className="modal-action">
|
||||
<button type="submit" className="btn">
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserInfoSettings
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useState } from 'react'
|
||||
import {useState} from 'react'
|
||||
|
||||
import { Tabs } from '../../components/mollecules'
|
||||
import {Tabs} from '../../components/mollecules'
|
||||
|
||||
import AccountSettings from './account/accountSettings'
|
||||
import HealthChecks from './healthchecks/healthChecks'
|
||||
import MoneyManagementSettings from './moneymanagement/moneyManagement'
|
||||
import Theme from './theme'
|
||||
import DefaultConfig from './defaultConfig/defaultConfig'
|
||||
import UserInfoSettings from './UserInfoSettings'
|
||||
|
||||
type TabsType = {
|
||||
label: string
|
||||
@@ -17,28 +18,33 @@ type TabsType = {
|
||||
// Tabs Array
|
||||
const tabs: TabsType = [
|
||||
{
|
||||
Component: MoneyManagementSettings,
|
||||
Component: UserInfoSettings,
|
||||
index: 1,
|
||||
label: 'User Info',
|
||||
},
|
||||
{
|
||||
Component: MoneyManagementSettings,
|
||||
index: 2,
|
||||
label: 'Money Management',
|
||||
},
|
||||
{
|
||||
Component: AccountSettings,
|
||||
index: 2,
|
||||
index: 3,
|
||||
label: 'Account Settings',
|
||||
},
|
||||
{
|
||||
Component: Theme,
|
||||
index: 3,
|
||||
index: 4,
|
||||
label: 'Theme',
|
||||
},
|
||||
{
|
||||
Component: DefaultConfig,
|
||||
index: 4,
|
||||
index: 5,
|
||||
label: 'Quick Start Config',
|
||||
},
|
||||
{
|
||||
Component: HealthChecks,
|
||||
index: 5,
|
||||
index: 6,
|
||||
label: 'Health Checks',
|
||||
},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user