diff --git a/assets/Todo-v2.md b/assets/Todo-v2.md index 889bcf9..ec89387 100644 --- a/assets/Todo-v2.md +++ b/assets/Todo-v2.md @@ -107,22 +107,6 @@ - [x] Add button to display money management use by the bot - [ ] POST POWNER - On the modarl, When simple bot selected, show only a select for the workflow -## Workflow - -- [x] List all workflow saved in -- [x] Use https://reactflow.dev/ to display a workflow (map flow to nodes and children to edges) -- [x] On update Nodes : https://codesandbox.io/s/dank-waterfall-8jfcf4?file=/src/App.js -- [x] Save workflow -- [ ] Reset workflow -- [ ] Add flows : Close Position, SendMessage -- [ ] On Flow.tsx : Display inputs/outputs names on the node -- [ ] Setup file tree UI for available flows : https://codesandbox.io/s/nlzui -- [x] Create a workflow type that will encapsulate a list of flows -- [x] Each flow will have parameters, inputs and outputs that will be used by the children flows -- [ ] Flow can handle multiple parents -- [ ] Run Simple bot base on a workflow -- [ ] Run backtest based on a workflow -- [ ] Add flows : ClosePosition, Scenario ## Backtests diff --git a/assets/documentation/Flows.md b/assets/documentation/Flows.md deleted file mode 100644 index c351076..0000000 --- a/assets/documentation/Flows.md +++ /dev/null @@ -1,27 +0,0 @@ -```mermaid -classDiagram - Workflow <|-- Flow - class Workflow{ - String Name - Usage Usage : Trading|Task - Flow[] Flows - String Description - } - class Flow{ - String Name - CategoryType Category - FlowType Type - FlowParameters Parameters - String Description - FlowType? AcceptedInput - OutputType[]? Outputs - Flow[]? ChildrenFlow - Flow? ParentFlow - Output? Output : Signal|Text|Candles - MapInput(AcceptedInput, ParentFlow.Output) - Run(ParentFlow.Output) - LoadChildren() - ExecuteChildren() - } - -``` \ No newline at end of file diff --git a/src/Managing.Api/Controllers/WorkflowController.cs b/src/Managing.Api/Controllers/WorkflowController.cs deleted file mode 100644 index f8a3aad..0000000 --- a/src/Managing.Api/Controllers/WorkflowController.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Managing.Application.Abstractions; -using Managing.Application.Abstractions.Services; -using Managing.Domain.Workflows; -using Managing.Domain.Workflows.Synthetics; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Managing.Api.Controllers -{ - /// - /// Controller for managing workflows, including creating, retrieving, and deleting workflows. - /// Requires authorization for access. - /// - [Authorize] - public class WorkflowController : BaseController - { - private readonly IWorkflowService _workflowService; - - /// - /// Initializes a new instance of the class. - /// - /// Service for managing workflows. - /// Service for user-related operations. - public WorkflowController(IWorkflowService WorkflowService, IUserService userService) : base(userService) - { - _workflowService = WorkflowService; - } - - /// - /// Creates a new workflow or updates an existing one based on the provided workflow request. - /// - /// The workflow request containing the details of the workflow to be created or updated. - /// The created or updated workflow. - [HttpPost] - public async Task> PostWorkflow([ModelBinder]SyntheticWorkflow workflowRequest) - { - return Ok(await _workflowService.InsertOrUpdateWorkflow(workflowRequest)); - } - - /// - /// Retrieves all workflows. - /// - /// A list of all workflows. - [HttpGet] - public ActionResult> GetWorkflows() - { - return Ok(_workflowService.GetWorkflows()); - } - - /// - /// Retrieves all available flows. - /// - /// A list of all available flows. - [HttpGet] - [Route("flows")] - public async Task>> GetAvailableFlows() - { - return Ok(await _workflowService.GetAvailableFlows()); - } - - /// - /// Deletes a workflow by name. - /// - /// The name of the workflow to delete. - /// An ActionResult indicating the outcome of the operation. - [HttpDelete] - public ActionResult DeleteWorkflow(string name) - { - return Ok(_workflowService.DeleteWorkflow(name)); - } - } -} \ No newline at end of file diff --git a/src/Managing.Application.Abstractions/Repositories/IWorkflowRepository.cs b/src/Managing.Application.Abstractions/Repositories/IWorkflowRepository.cs deleted file mode 100644 index 85e0ba4..0000000 --- a/src/Managing.Application.Abstractions/Repositories/IWorkflowRepository.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Managing.Domain.Workflows.Synthetics; - -namespace Managing.Application.Abstractions.Repositories; - -public interface IWorkflowRepository -{ - bool DeleteWorkflow(string name); - Task GetWorkflow(string name); - IEnumerable GetWorkflows(); - Task InsertWorkflow(SyntheticWorkflow workflow); - Task UpdateWorkflow(SyntheticWorkflow workflow); -} diff --git a/src/Managing.Application.Tests/WorkflowTests.cs b/src/Managing.Application.Tests/WorkflowTests.cs deleted file mode 100644 index 5c6d506..0000000 --- a/src/Managing.Application.Tests/WorkflowTests.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Managing.Domain.Workflows; -using Xunit; -using static Managing.Common.Enums; - -namespace Managing.Application.Tests; - -public class WorkflowTests : BaseTests -{ - [Fact] - public async Task Should_Create_Workflow_with_Feed_Ticker_Flow() - { - // Arrange - var workflow = new Workflow - { - Name = "Bot trading", - Usage = WorkflowUsage.Trading, - Description = "Basic trading Workflow", - Flows = new List() - }; - - // var rsiDivFlow = new RsiDiv() - // { - // Parameters = "{\"Period\": 14,\"Timeframe\":1}", - // Children = new List(), - // }; - - // var tickerFlow = new FeedTicker(_exchangeService) - // { - // Parameters = "{\"Exchange\": 3,\"Ticker\":9,\"Timeframe\":1}", - // Children = new List() - // { - // rsiDivFlow - // } - // }; - - // workflow.Flows.Add(tickerFlow); - - // Act - await workflow.Execute(); - - // Assert - foreach (var f in workflow.Flows) - { - Assert.False(string.IsNullOrEmpty(f.Output)); - } - - Assert.NotNull(workflow); - Assert.NotNull(workflow.Flows); - Assert.Single(workflow.Flows); - Assert.Equal("Feed Ticker", workflow.Name); - Assert.Equal(WorkflowUsage.Trading, workflow.Usage); - Assert.Equal("Basic trading Workflow", workflow.Description); - } -} \ No newline at end of file diff --git a/src/Managing.Application/Abstractions/IFlowFactory.cs b/src/Managing.Application/Abstractions/IFlowFactory.cs deleted file mode 100644 index a7c6e8d..0000000 --- a/src/Managing.Application/Abstractions/IFlowFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Managing.Domain.Workflows; -using Managing.Domain.Workflows.Synthetics; - -namespace Managing.Application.Abstractions; - -public interface IFlowFactory -{ - IFlow BuildFlow(SyntheticFlow request); -} diff --git a/src/Managing.Application/Abstractions/IWorkflowService.cs b/src/Managing.Application/Abstractions/IWorkflowService.cs deleted file mode 100644 index 18165d4..0000000 --- a/src/Managing.Application/Abstractions/IWorkflowService.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Managing.Domain.Workflows; -using Managing.Domain.Workflows.Synthetics; - -namespace Managing.Application.Abstractions; - -public interface IWorkflowService -{ - bool DeleteWorkflow(string name); - Task> GetAvailableFlows(); - IEnumerable GetWorkflows(); - Task InsertOrUpdateWorkflow(SyntheticWorkflow workflowRequest); -} diff --git a/src/Managing.Domain/Workflows/FlowBase.cs b/src/Managing.Domain/Workflows/FlowBase.cs deleted file mode 100644 index c6efc50..0000000 --- a/src/Managing.Domain/Workflows/FlowBase.cs +++ /dev/null @@ -1,19 +0,0 @@ -using static Managing.Common.Enums; - -namespace Managing.Domain.Workflows; - -public abstract class FlowBase : IFlow -{ - public abstract Guid Id { get; } - public abstract string Name { get; } - public abstract FlowType Type { get; } - public abstract string Description { get; } - public abstract List AcceptedInputs { get; } - public abstract List Children { get; set; } - public abstract List Parameters { get; set; } - public abstract Guid ParentId { get; } - public abstract string Output { get; set; } - public abstract List OutputTypes { get; } - public abstract Task Execute(string input); - public abstract void MapParameters(); -} diff --git a/src/Managing.Domain/Workflows/FlowParameter.cs b/src/Managing.Domain/Workflows/FlowParameter.cs deleted file mode 100644 index aeff619..0000000 --- a/src/Managing.Domain/Workflows/FlowParameter.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Managing.Domain.Workflows; - -public class FlowParameter -{ - public dynamic Value { get; set; } - public string Name { get; set; } -} diff --git a/src/Managing.Domain/Workflows/IFlow.cs b/src/Managing.Domain/Workflows/IFlow.cs deleted file mode 100644 index 8564bf3..0000000 --- a/src/Managing.Domain/Workflows/IFlow.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using static Managing.Common.Enums; - -namespace Managing.Domain.Workflows; - -public interface IFlow -{ - [Required] - Guid Id { get; } - [Required] - string Name { get; } - [Required] - FlowType Type { get; } - [Required] - string Description { get; } - [Required] - List AcceptedInputs { get; } - List Children { get; set; } - [Required] - List Parameters { get; set; } - Guid ParentId { get; } - string Output { get; set; } - [Required] - List OutputTypes { get; } - Task Execute(string input); -} diff --git a/src/Managing.Domain/Workflows/Synthetics/SyntheticFlow.cs b/src/Managing.Domain/Workflows/Synthetics/SyntheticFlow.cs deleted file mode 100644 index cf0ec11..0000000 --- a/src/Managing.Domain/Workflows/Synthetics/SyntheticFlow.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using static Managing.Common.Enums; - -namespace Managing.Domain.Workflows.Synthetics; - -public class SyntheticFlow -{ - [Required] - public string Id { get; set; } - public string ParentId { get; set; } - [Required] - public FlowType Type { get; set; } - [Required] - public List Parameters { get; set; } -} diff --git a/src/Managing.Domain/Workflows/Synthetics/SyntheticFlowParameter.cs b/src/Managing.Domain/Workflows/Synthetics/SyntheticFlowParameter.cs deleted file mode 100644 index dd83ff2..0000000 --- a/src/Managing.Domain/Workflows/Synthetics/SyntheticFlowParameter.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Managing.Domain.Workflows.Synthetics; - -public class SyntheticFlowParameter -{ - [Required] - public string Value { get; set; } - [Required] - public string Name { get; set; } -} \ No newline at end of file diff --git a/src/Managing.Domain/Workflows/Synthetics/SyntheticWorkflow.cs b/src/Managing.Domain/Workflows/Synthetics/SyntheticWorkflow.cs deleted file mode 100644 index 3834230..0000000 --- a/src/Managing.Domain/Workflows/Synthetics/SyntheticWorkflow.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using static Managing.Common.Enums; - -namespace Managing.Domain.Workflows.Synthetics; - -public class SyntheticWorkflow -{ - [Required] - public string Name { get; set; } - [Required] - public WorkflowUsage Usage { get; set; } - [Required] - public string Description { get; set; } - [Required] - public List Flows { get; set; } -} diff --git a/src/Managing.Domain/Workflows/Workflow.cs b/src/Managing.Domain/Workflows/Workflow.cs deleted file mode 100644 index de5e3be..0000000 --- a/src/Managing.Domain/Workflows/Workflow.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using static Managing.Common.Enums; - -namespace Managing.Domain.Workflows; - -public class Workflow -{ - [Required] - public string Name { get; set; } - [Required] - public WorkflowUsage Usage { get; set; } - [Required] - public List Flows { get; set; } - [Required] - public string Description { get; set; } - - public async Task Execute() - { - foreach (var flow in Flows) - { - await flow.Execute(string.Empty); - } - } -} diff --git a/src/Managing.WebApp/src/app/routes/index.tsx b/src/Managing.WebApp/src/app/routes/index.tsx index da67c32..3c196cd 100644 --- a/src/Managing.WebApp/src/app/routes/index.tsx +++ b/src/Managing.WebApp/src/app/routes/index.tsx @@ -1,11 +1,10 @@ -import { Suspense, lazy } from 'react' -import { Route, Routes } from 'react-router-dom' +import {lazy, Suspense} from 'react' +import {Route, Routes} from 'react-router-dom' import LayoutMain from '../../layouts' import DeskWidget from '../../pages/desk/deskWidget' import Scenario from '../../pages/scenarioPage/scenario' import Tools from '../../pages/toolsPage/tools' -import Workflows from '../../pages/workflow/workflows' const Backtest = lazy(() => import('../../pages/backtestPage/backtest')) const Bots = lazy(() => import('../../pages/botsPage/bots')) @@ -48,17 +47,6 @@ const MyRoutes = () => { /> - }> - - - - } - /> - - }> void - getFlows: (apiUrl: string) => void - flows: IFlow[] -} - -export const useFlowStore = create((set) => ({ - flows: [] as IFlow[], - getFlows: async (apiUrl) => { - const client = new WorkflowClient({}, apiUrl) - await client.workflow_GetAvailableFlows().then((data) => { - set(() => ({ - flows: data, - })) - }) - }, - setFlows: (flows) => { - set((state) => ({ - ...state, - flows: flows, - })) - }, -})) diff --git a/src/Managing.WebApp/src/app/store/selectors/workflowSelector.tsx b/src/Managing.WebApp/src/app/store/selectors/workflowSelector.tsx deleted file mode 100644 index 5f8f754..0000000 --- a/src/Managing.WebApp/src/app/store/selectors/workflowSelector.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { IWorkflowStore } from '../workflowStore' - -export const WorkflowSelector = (state: IWorkflowStore) => ({ - edges: state.edges, - initWorkFlow: state.initWorkFlow, - nodes: state.nodes, - onConnect: state.onConnect, - onEdgesChange: state.onEdgesChange, - onNodesChange: state.onNodesChange, - resetWorkflow: state.resetWorkflow, - setNodes: state.setNodes, - updateNodeData: state.updateNodeData, -}) diff --git a/src/Managing.WebApp/src/app/store/workflowStore.tsx b/src/Managing.WebApp/src/app/store/workflowStore.tsx deleted file mode 100644 index 834b72a..0000000 --- a/src/Managing.WebApp/src/app/store/workflowStore.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import type { - Connection, - Edge, - EdgeChange, - Node, - NodeChange, - OnNodesChange, - OnEdgesChange, - OnConnect, -} from 'reactflow' -import { addEdge, applyNodeChanges, applyEdgeChanges } from 'reactflow' -import { create } from 'zustand' - -import type { - SyntheticFlowParameter, - FlowParameter, - IFlow, -} from '../../generated/ManagingApi' - -export type IWorkflowStore = { - nodes: Node[] - initialNodes: Node[] - edges: Edge[] - initialEdges: Edge[] - onNodesChange: OnNodesChange - onEdgesChange: OnEdgesChange - onConnect: OnConnect - updateNodeData: (nodeId: string, parameterName: string, value: string) => void - initWorkFlow: (nodes: Node[], edges: Edge[]) => void - setNodes: (nodes: Node[]) => void - resetWorkflow: () => void -} - -// this is our useStore hook that we can use in our components to get parts of the store and call actions -const useWorkflowStore = create((set, get) => ({ - edges: [], - initWorkFlow: (nodes: Node[], edges: Edge[]) => { - set({ - edges: edges, - initialEdges: edges, - initialNodes: nodes, - nodes: nodes, - }) - }, - initialEdges: [], - initialNodes: [], - nodes: [], - onConnect: (connection: Connection, callback: void) => { - set({ - edges: addEdge(connection, get().edges), - }) - }, - onEdgesChange: (changes: EdgeChange[]) => { - set({ - edges: applyEdgeChanges(changes, get().edges), - }) - }, - onNodesChange: (changes: NodeChange[]) => { - set({ - nodes: applyNodeChanges(changes, get().nodes), - }) - }, - resetWorkflow: () => { - set({ - edges: get().initialEdges, - initialEdges: get().initialEdges, - initialNodes: get().initialNodes, - nodes: get().initialNodes, - }) - }, - setNodes: (nodes: Node[]) => { - set({ - nodes: nodes, - }) - }, - updateNodeData: (nodeId: string, parameterName: string, value: string) => { - set({ - nodes: get().nodes.map((node) => { - if (node.id === nodeId) { - node.data.parameters = updateParameters( - node.data.parameters, - parameterName, - value - ) - } - return node - }), - }) - }, -})) - -const updateParameters = ( - parameters: FlowParameter[], - name: string, - value: string -) => { - if (!parameters.find((parameter) => parameter.name === name)) { - parameters.push({ - name: name, - value: value, - } as SyntheticFlowParameter) - } else { - parameters = parameters.map((parameter) => { - if (parameter.name === name) { - parameter.value = value - } - return parameter - }) - } - return parameters -} - -export default useWorkflowStore diff --git a/src/Managing.WebApp/src/components/organism/Workflow/flowItem.tsx b/src/Managing.WebApp/src/components/organism/Workflow/flowItem.tsx deleted file mode 100644 index b63ebf7..0000000 --- a/src/Managing.WebApp/src/components/organism/Workflow/flowItem.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react' - -import type { IFlow } from '../../../generated/ManagingApi' - -type IFlowItem = { - onDragStart: (event: any, data: string) => void - flow: IFlow -} - -const FlowItem: React.FC = ({ onDragStart, flow }) => { - return ( -
onDragStart(event, `${flow.type}`)} - draggable - > - {flow.name} -
- ) -} - -export default FlowItem diff --git a/src/Managing.WebApp/src/components/organism/Workflow/flows/Flow.tsx b/src/Managing.WebApp/src/components/organism/Workflow/flows/Flow.tsx deleted file mode 100644 index 3f86f01..0000000 --- a/src/Managing.WebApp/src/components/organism/Workflow/flows/Flow.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Handle, Position } from 'reactflow' - -import type { FlowOutput } from '../../../../generated/ManagingApi' -import type { IFlowProps } from '../../../../global/type' -import { Card } from '../../../mollecules' - -const Flow = ({ - name, - description, - children, - inputs, - outputs, - isConnectable, -}: IFlowProps) => { - return ( - - {inputs?.map((input: FlowOutput) => { - return ( - - ) - })} - {children} - {outputs?.map((output: FlowOutput) => { - return ( - - ) - })} - - ) -} - -export default Flow diff --git a/src/Managing.WebApp/src/components/organism/Workflow/flows/feed/feedTicker.tsx b/src/Managing.WebApp/src/components/organism/Workflow/flows/feed/feedTicker.tsx deleted file mode 100644 index 8ac1132..0000000 --- a/src/Managing.WebApp/src/components/organism/Workflow/flows/feed/feedTicker.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useCallback } from 'react' -import type { NodeProps } from 'reactflow' - -import { WorkflowSelector } from '../../../../../app/store/selectors/workflowSelector' -import useWorkflowStore from '../../../../../app/store/workflowStore' -import type { IFlow } from '../../../../../generated/ManagingApi' -import { Timeframe, Ticker } from '../../../../../generated/ManagingApi' -import { FormInput } from '../../../../mollecules' -import Flow from '../Flow' - -const FeedTicker = ({ data, isConnectable, id }: NodeProps) => { - const { updateNodeData } = useWorkflowStore(WorkflowSelector) - - const onTickerChange = useCallback((evt: any) => { - updateNodeData(id, 'Ticker', evt.target.value) - }, []) - - const onTimeframeChange = useCallback((evt: any) => { - updateNodeData(id, 'Timeframe', evt.target.value) - }, []) - - return ( - - - - - - - - - ) -} - -export default FeedTicker diff --git a/src/Managing.WebApp/src/components/organism/Workflow/flows/strategies/RsiDivergenceFlow.tsx b/src/Managing.WebApp/src/components/organism/Workflow/flows/strategies/RsiDivergenceFlow.tsx deleted file mode 100644 index 1ead64d..0000000 --- a/src/Managing.WebApp/src/components/organism/Workflow/flows/strategies/RsiDivergenceFlow.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useCallback } from 'react' -import type { NodeProps } from 'reactflow' - -import { WorkflowSelector } from '../../../../../app/store/selectors/workflowSelector' -import useWorkflowStore from '../../../../../app/store/workflowStore' -import type { IFlow } from '../../../../../generated/ManagingApi' -import { Timeframe } from '../../../../../generated/ManagingApi' -import { FormInput } from '../../../../mollecules' -import Flow from '../Flow' - -const RsiDivergenceFlow = ({ data, isConnectable, id }: NodeProps) => { - const { updateNodeData } = useWorkflowStore(WorkflowSelector) - - const onPeriodChange = useCallback((evt: any) => { - updateNodeData(id, 'Period', evt.target.value) - }, []) - - const onTimeframeChange = useCallback((evt: any) => { - updateNodeData(id, 'Timeframe', evt.target.value) - }, []) - - return ( - - - p.name === 'Period')?.value || ''} - /> - - - - - - ) -} - -export default RsiDivergenceFlow diff --git a/src/Managing.WebApp/src/components/organism/Workflow/flows/trading/OpenPositionFlow.tsx b/src/Managing.WebApp/src/components/organism/Workflow/flows/trading/OpenPositionFlow.tsx deleted file mode 100644 index 13c15dc..0000000 --- a/src/Managing.WebApp/src/components/organism/Workflow/flows/trading/OpenPositionFlow.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import React, { useCallback, useState } from 'react' -import type { NodeProps } from 'reactflow' - -import useApiUrlStore from '../../../../../app/store/apiStore' -import { WorkflowSelector } from '../../../../../app/store/selectors/workflowSelector' -import useWorkflowStore from '../../../../../app/store/workflowStore' -import type { Account, IFlow } from '../../../../../generated/ManagingApi' -import { MoneyManagementClient } from '../../../../../generated/ManagingApi' -import useAccounts from '../../../../../hooks/useAccounts' -import { Loader } from '../../../../atoms' -import { FormInput } from '../../../../mollecules' -import Flow from '../Flow' - -const OpenPositionFlow = ({ data, isConnectable, id }: NodeProps) => { - const { updateNodeData } = useWorkflowStore(WorkflowSelector) - const { apiUrl } = useApiUrlStore() - - const [selectedAccount, setSelectedAccount] = React.useState() - const [selectedMoneyManagement, setSelectedMoneyManagement] = - useState() - const moneyManagementClient = new MoneyManagementClient({}, apiUrl) - - const { data: accounts } = useAccounts({ - callback: (data: Account[]) => { - setSelectedAccount(data[0].name) - }, - }) - - const { data: moneyManagements } = useQuery({ - onSuccess: (data) => { - if (data) { - setSelectedMoneyManagement(data[0].name) - } - }, - queryFn: () => moneyManagementClient.moneyManagement_GetMoneyManagements(), - queryKey: ['moneyManagement'], - }) - - const onAccountChange = useCallback((evt: any) => { - updateNodeData(id, 'Account', evt.target.value) - }, []) - - const onMoneyManagementChange = useCallback((evt: any) => { - updateNodeData(id, 'MoneyManagement', evt.target.value) - }, []) - - if (!accounts || !moneyManagements) { - return - } - - return ( - - - - - - - - - ) -} - -export default OpenPositionFlow diff --git a/src/Managing.WebApp/src/components/organism/Workflow/flows/trading/test.tsx b/src/Managing.WebApp/src/components/organism/Workflow/flows/trading/test.tsx deleted file mode 100644 index bd3aa0d..0000000 --- a/src/Managing.WebApp/src/components/organism/Workflow/flows/trading/test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useQuery } from "@tanstack/react-query" - -const fetchData = () => { - return { - fake: 'data', - } -} - -const ParentComponent = () => { - const { data, isLoading } = useQuery({ - queryFn: fetchData, - queryKey: ['data'], - }) - - if (isLoading) { - return
Loading...
- } - - return ( -
- -
- ) -} diff --git a/src/Managing.WebApp/src/components/organism/Workflow/workflowCanvas.tsx b/src/Managing.WebApp/src/components/organism/Workflow/workflowCanvas.tsx deleted file mode 100644 index d26f045..0000000 --- a/src/Managing.WebApp/src/components/organism/Workflow/workflowCanvas.tsx +++ /dev/null @@ -1,255 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { useCallback, useMemo, useRef, useState } from 'react' -import type { NodeTypes, Node } from 'reactflow' -import ReactFlow, { Controls, Background, ReactFlowProvider } from 'reactflow' - -import 'reactflow/dist/style.css' - -import useApiUrlStore from '../../../app/store/apiStore' -import { WorkflowSelector } from '../../../app/store/selectors/workflowSelector' -import useWorkflowStore from '../../../app/store/workflowStore' -import type { - FlowType, - IFlow, - SyntheticFlow, - SyntheticFlowParameter, - SyntheticWorkflow, -} from '../../../generated/ManagingApi' -import { WorkflowUsage, WorkflowClient } from '../../../generated/ManagingApi' -import type { IWorkflow, IFlowItem } from '../../../global/type' -import { Toast } from '../../mollecules' - -import FeedTicker from './flows/feed/feedTicker' -import RsiDivergenceFlow from './flows/strategies/RsiDivergenceFlow' -import OpenPositionFlow from './flows/trading/OpenPositionFlow' -import WorkflowForm from './workflowForm' -import WorkflowSidebar from './workflowSidebar' - -let id = 0 -const getId = () => `dndnode_${id++}` - -const mapToFlowRequest = ( - flow: IFlow, - id: string, - parentId: string -): SyntheticFlow => { - return { - id: id, - parameters: flow.parameters as SyntheticFlowParameter[], - parentId: parentId, - type: flow.type, - } -} - -const WorkflowCanvas: React.FC = (props: any) => { - const tabs = props - const properties = tabs['data-props'] as IWorkflow - const reactFlowWrapper = useRef(null) - const [reactFlowInstance, setReactFlowInstance] = useState(null) - const [isUpdated, setIsUpdated] = useState(false) - const { apiUrl } = useApiUrlStore() - const [usage, setUsage] = useState(WorkflowUsage.Trading) - const [name, setName] = useState(properties.name) - const client = new WorkflowClient({}, apiUrl) - - const nodeTypes: NodeTypes = useMemo( - () => ({ - FeedTicker: FeedTicker, - OpenPosition: OpenPositionFlow, - RsiDivergence: RsiDivergenceFlow, - }), - [] - ) - - const { - nodes, - edges, - onNodesChange, - onEdgesChange, - onConnect, - initWorkFlow, - setNodes, - resetWorkflow, - } = useWorkflowStore(WorkflowSelector) - - const { data, isLoading } = useQuery({ - onSuccess: (data) => { - initWorkFlow([], properties.edges) - if (data) { - setNodes(properties.nodes.map((n, i) => mapToNodeItem(n, data, i))) - } - }, - queryFn: () => client.workflow_GetAvailableFlows(), - queryKey: ['availableFlows'], - }) - - const mapToNodeItem = ( - flow: Node, - availableFlows: IFlow[], - index: number - ): IFlowItem => { - const nodeData = availableFlows.find((f) => f.type === flow.type) - - if (nodeData == null) { - return {} as IFlowItem - } - - nodeData.parameters = flow.data.parameters - - return { - data: nodeData, - id: flow.id, - isConnectable: nodeData.acceptedInputs?.length > 0, - position: { x: index * 400, y: index * 100 }, - type: flow.type as FlowType, - } - } - - const isValidConnection = (connection: any) => { - if (reactFlowInstance == null) { - return false - } - - const sourceData: IFlow = reactFlowInstance.getNode(connection.source).data - const targetData: IFlow = reactFlowInstance.getNode(connection.target).data - - return sourceData.outputTypes?.some( - (output) => targetData.acceptedInputs?.indexOf(output) >= 0 - ) - } - - const handleOnConnect = useCallback((params: any) => { - setIsUpdated(true) - onConnect(params) - }, []) - - const onDragOver = useCallback((event: any) => { - event.preventDefault() - event.dataTransfer.dropEffect = 'move' - }, []) - - const onDrop = useCallback( - (event: any) => { - event.preventDefault() - - if (reactFlowInstance == null || reactFlowWrapper.current == null) { - return - } - - const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect() - const data = event.dataTransfer.getData('application/reactflow') - if (typeof data === 'undefined' || !data) { - return - } - - const properties: string[] = data.split('-') - const position = reactFlowInstance.project({ - x: event.clientX - reactFlowBounds.left, - y: event.clientY - reactFlowBounds.top, - }) - - const flow = flows.find((flow) => flow.type === properties[0]) - const newNode: IFlowItem = { - data: flow || ({ parameters: [] as SyntheticFlowParameter[] } as IFlow), - id: getId(), - isConnectable: - (flow?.acceptedInputs && flow?.acceptedInputs?.length > 0) ?? false, - position, - type: properties[0] as FlowType, - } - - newNode.data.parameters = [] - setNodes(nodes.concat(newNode)) - }, - [reactFlowInstance, data, nodes] - ) - - const handleOnSave = () => { - const t = new Toast('Saving workflow') - const nodesRequest: SyntheticFlow[] = [] - - const client = new WorkflowClient({}, apiUrl) - - if (edges.length <= 0) { - t.update('error', 'Workflow must have at least one edge') - return - } - - for (const node of nodes) { - const data: IFlow = reactFlowInstance.getNode(node.id).data - - const parentId = edges.find((edge) => edge.target === node.id)?.source - nodesRequest.push(mapToFlowRequest(data, node.id, parentId || '')) - } - - const request: SyntheticWorkflow = { - description: 'Test', - flows: nodesRequest, - name: properties.name, - usage: usage, - } - - client - .workflow_PostWorkflow(request) - .then((data) => { - t.update('success', 'Workflow saved') - }) - .catch((err) => { - t.update('error', 'Error :' + err) - }) - } - - const handleOnReset = () => { - resetWorkflow() - } - - const handleUsageChange = (event: any) => { - setUsage(event.target.value) - } - - return ( - <> - - -
- -
- -
-
- - - - -
-
-
- - ) -} - -export default WorkflowCanvas diff --git a/src/Managing.WebApp/src/components/organism/Workflow/workflowForm.tsx b/src/Managing.WebApp/src/components/organism/Workflow/workflowForm.tsx deleted file mode 100644 index d63dd96..0000000 --- a/src/Managing.WebApp/src/components/organism/Workflow/workflowForm.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { WorkflowUsage } from '../../../generated/ManagingApi' -import { FormInput } from '../../mollecules' - -type IWorkflowForm = { - name: string - usage: WorkflowUsage - handleUsageChange: (evt: any) => void - handleOnSave: () => void - handleOnReset: () => void - isUpdated: boolean - setName: (name: string) => void -} - -const WorkflowForm = ({ - name, - handleUsageChange, - handleOnSave, - isUpdated, - handleOnReset, - setName, -}: IWorkflowForm) => { - return ( - <> -
-
- - setName(evt.target.value)} - className="nodrag input" - value={name} - /> - - - - -
-
- - -
-
- - ) -} - -export default WorkflowForm diff --git a/src/Managing.WebApp/src/components/organism/Workflow/workflowSidebar.tsx b/src/Managing.WebApp/src/components/organism/Workflow/workflowSidebar.tsx deleted file mode 100644 index b1cab0f..0000000 --- a/src/Managing.WebApp/src/components/organism/Workflow/workflowSidebar.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { IFlow } from '../../../generated/ManagingApi' -import { Loader } from '../../atoms' - -import FlowItem from './flowItem' - -type IWorkflowSidebar = { - flows?: IFlow[] - isLoading: boolean -} - -const WorkflowSidebar = ({ flows, isLoading = true }: IWorkflowSidebar) => { - const onDragStart = (event: any, data: string) => { - event.dataTransfer.setData('application/reactflow', data) - event.dataTransfer.effectAllowed = 'move' - } - - if (isLoading) { - return - } - - return ( - - ) -} - -export default WorkflowSidebar diff --git a/src/Managing.WebApp/src/generated/ManagingApi.ts b/src/Managing.WebApp/src/generated/ManagingApi.ts index e75e799..b96825f 100644 --- a/src/Managing.WebApp/src/generated/ManagingApi.ts +++ b/src/Managing.WebApp/src/generated/ManagingApi.ts @@ -3527,169 +3527,6 @@ export class UserClient extends AuthorizedApiBase { } } -export class WorkflowClient extends AuthorizedApiBase { - private http: { fetch(url: RequestInfo, init?: RequestInit): Promise }; - private baseUrl: string; - protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined; - - constructor(configuration: IConfig, baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) { - super(configuration); - this.http = http ? http : window as any; - this.baseUrl = baseUrl ?? "http://localhost:5000"; - } - - workflow_PostWorkflow(workflowRequest: SyntheticWorkflow): Promise { - let url_ = this.baseUrl + "/Workflow"; - url_ = url_.replace(/[?&]$/, ""); - - const content_ = JSON.stringify(workflowRequest); - - let options_: RequestInit = { - body: content_, - method: "POST", - 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.processWorkflow_PostWorkflow(_response); - }); - } - - protected processWorkflow_PostWorkflow(response: Response): Promise { - 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 Workflow; - 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(null as any); - } - - workflow_GetWorkflows(): Promise { - let url_ = this.baseUrl + "/Workflow"; - 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.processWorkflow_GetWorkflows(_response); - }); - } - - protected processWorkflow_GetWorkflows(response: Response): Promise { - 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 SyntheticWorkflow[]; - 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(null as any); - } - - workflow_DeleteWorkflow(name: string | null | undefined): Promise { - let url_ = this.baseUrl + "/Workflow?"; - if (name !== undefined && name !== null) - url_ += "name=" + encodeURIComponent("" + name) + "&"; - url_ = url_.replace(/[?&]$/, ""); - - let options_: RequestInit = { - method: "DELETE", - headers: { - "Accept": "application/octet-stream" - } - }; - - return this.transformOptions(options_).then(transformedOptions_ => { - return this.http.fetch(url_, transformedOptions_); - }).then((_response: Response) => { - return this.processWorkflow_DeleteWorkflow(_response); - }); - } - - protected processWorkflow_DeleteWorkflow(response: Response): Promise { - 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 }; }); - } else if (status !== 200 && status !== 204) { - return response.text().then((_responseText) => { - return throwException("An unexpected server error occurred.", status, _responseText, _headers); - }); - } - return Promise.resolve(null as any); - } - - workflow_GetAvailableFlows(): Promise { - let url_ = this.baseUrl + "/Workflow/flows"; - 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.processWorkflow_GetAvailableFlows(_response); - }); - } - - protected processWorkflow_GetAvailableFlows(response: Response): Promise { - 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 IFlow[]; - 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(null as any); - } -} - export interface Account { name: string; exchange: TradingExchanges; @@ -4796,68 +4633,6 @@ export interface LoginRequest { message: string; } -export interface Workflow { - name: string; - usage: WorkflowUsage; - flows: IFlow[]; - description: string; -} - -export enum WorkflowUsage { - Trading = "Trading", - Task = "Task", -} - -export interface IFlow { - id: string; - name: string; - type: FlowType; - description: string; - acceptedInputs: FlowOutput[]; - children?: IFlow[] | null; - parameters: FlowParameter[]; - parentId?: string; - output?: string | null; - outputTypes: FlowOutput[]; -} - -export enum FlowType { - RsiDivergence = "RsiDivergence", - FeedTicker = "FeedTicker", - OpenPosition = "OpenPosition", -} - -export enum FlowOutput { - Signal = "Signal", - Candles = "Candles", - Position = "Position", - MoneyManagement = "MoneyManagement", -} - -export interface FlowParameter { - value?: any | null; - name?: string | null; -} - -export interface SyntheticWorkflow { - name: string; - usage: WorkflowUsage; - description: string; - flows: SyntheticFlow[]; -} - -export interface SyntheticFlow { - id: string; - parentId?: string | null; - type: FlowType; - parameters: SyntheticFlowParameter[]; -} - -export interface SyntheticFlowParameter { - value: string; - name: string; -} - export interface FileResponse { data: Blob; status: number; diff --git a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts index 7cc5fca..10f45e2 100644 --- a/src/Managing.WebApp/src/generated/ManagingApiTypes.ts +++ b/src/Managing.WebApp/src/generated/ManagingApiTypes.ts @@ -1116,68 +1116,6 @@ export interface LoginRequest { message: string; } -export interface Workflow { - name: string; - usage: WorkflowUsage; - flows: IFlow[]; - description: string; -} - -export enum WorkflowUsage { - Trading = "Trading", - Task = "Task", -} - -export interface IFlow { - id: string; - name: string; - type: FlowType; - description: string; - acceptedInputs: FlowOutput[]; - children?: IFlow[] | null; - parameters: FlowParameter[]; - parentId?: string; - output?: string | null; - outputTypes: FlowOutput[]; -} - -export enum FlowType { - RsiDivergence = "RsiDivergence", - FeedTicker = "FeedTicker", - OpenPosition = "OpenPosition", -} - -export enum FlowOutput { - Signal = "Signal", - Candles = "Candles", - Position = "Position", - MoneyManagement = "MoneyManagement", -} - -export interface FlowParameter { - value?: any | null; - name?: string | null; -} - -export interface SyntheticWorkflow { - name: string; - usage: WorkflowUsage; - description: string; - flows: SyntheticFlow[]; -} - -export interface SyntheticFlow { - id: string; - parentId?: string | null; - type: FlowType; - parameters: SyntheticFlowParameter[]; -} - -export interface SyntheticFlowParameter { - value: string; - name: string; -} - export interface FileResponse { data: Blob; status: number; diff --git a/src/Managing.WebApp/src/global/type.tsx b/src/Managing.WebApp/src/global/type.tsx index 43105a7..1811fa4 100644 --- a/src/Managing.WebApp/src/global/type.tsx +++ b/src/Managing.WebApp/src/global/type.tsx @@ -1,21 +1,16 @@ import type {TableInstance, UsePaginationInstanceProps, UsePaginationState, UseSortByInstanceProps,} from 'react-table' -import type {Edge, Node} from 'reactflow' import type { Account, AccountType, Backtest, Balance, - BotType, - FlowOutput, - FlowType, - IFlow, IndicatorViewModel, + LightSignal, MoneyManagement, Position, RiskLevel, ScenarioViewModel, - Signal, Ticker, Timeframe, TradeDirection, @@ -36,48 +31,6 @@ export type TableInstanceWithHooks = TableInstance & state: UsePaginationState } -export type IWidgetProperties = { - id: string - title: string - layout: { - h: number - i: string - w: number - x: number - y: number - minW: number - minH: number - } -} - -export type IFlowData = { - name: string - type: string -} - -export type IFlowItem = { - data: IFlow - isConnectable: boolean - id: string - position: any - type: FlowType -} - -export type IFlowProps = { - name: string - description?: string - children: React.ReactNode - inputs: FlowOutput[] - outputs: FlowOutput[] - isConnectable: boolean -} - -export type IWorkflow = { - name: string - nodes: Node[] - edges: Edge[] -} - export type ILoginFormInput = { name: string } @@ -106,7 +59,6 @@ export type ISpotlightBadge = { export type IBacktestsFormInput = { accountName: string tickers: string[] - botType: BotType timeframe: Timeframe scenarioName: string save: boolean @@ -161,7 +113,6 @@ export type IMoneyManagementModalProps = { export type IBacktestFormInput = { accountName: string - botType: BotType ticker: Ticker timeframe: Timeframe save: boolean @@ -262,7 +213,7 @@ export type ICardPosition = { } export type ICardSignal = { - signals: Signal[] + signals: LightSignal[] } export type INavItemProps = { diff --git a/src/Managing.WebApp/src/pages/workflow/workflows.tsx b/src/Managing.WebApp/src/pages/workflow/workflows.tsx deleted file mode 100644 index 205b728..0000000 --- a/src/Managing.WebApp/src/pages/workflow/workflows.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { useEffect, useState } from 'react' -import type { Edge, Node } from 'reactflow' - -import useApiUrlStore from '../../app/store/apiStore' -import { Loader } from '../../components/atoms' -import { Tabs } from '../../components/mollecules' -import { WorkflowCanvas } from '../../components/organism' -import type { - IFlow, - SyntheticFlow, - SyntheticWorkflow, -} from '../../generated/ManagingApi' -import { WorkflowClient } from '../../generated/ManagingApi' -import type { ITabsType, IWorkflow } from '../../global/type' - -const mapWorkflowToTabs = (workflows: SyntheticWorkflow[]): ITabsType => { - return workflows.map((workflow: SyntheticWorkflow, index: number) => { - return { - Component: WorkflowCanvas, - index: index, - label: workflow.name, - props: mapFlowsToNodes(workflow.flows, workflow.name), - } - }) -} - -const mapFlowsToNodes = (flows: SyntheticFlow[], name: string): IWorkflow => { - const nodes: Node[] = [] - const edges: Edge[] = [] - - flows.forEach((flow: SyntheticFlow) => { - nodes.push(mapFlowToNode(flow)) - }) - - for (const node of nodes) { - const childrenNodes = nodes.filter((n) => n.data.parentId == node.data.id) - - if (childrenNodes.length > 0) { - childrenNodes.forEach((childNode) => { - edges.push({ - id: `${node.id}-${childNode.id}`, - source: node.id, - target: childNode.id, - }) - }) - } - } - - return { edges, name: name, nodes } as IWorkflow -} - -const mapFlowToNode = (flow: SyntheticFlow): Node => { - return { - data: flow, - id: flow.id, - position: { x: 0, y: 0 }, - type: flow.type, - } -} - -const Workflows: React.FC = () => { - const [selectedTab, setSelectedTab] = useState(1) - const { apiUrl } = useApiUrlStore() - const client = new WorkflowClient({}, apiUrl) - - const { data, isLoading } = useQuery({ - onSuccess: () => { - setSelectedTab(0) - }, - queryFn: () => client.workflow_GetWorkflows(), - queryKey: ['workflows'], - }) - - useEffect(() => {}, [isLoading]) - - if (isLoading || data == null) { - return - } - - return ( -
-
- { - console.log('add button clicked') - }} - /> -
-
- ) -} - -export default Workflows