diff --git a/.cursor/commands/optimize-current-code.md b/.cursor/commands/optimize-current-code.md new file mode 100644 index 00000000..6d14225b --- /dev/null +++ b/.cursor/commands/optimize-current-code.md @@ -0,0 +1,693 @@ +# optimize-current-code + +## When to Use + +Use this command when you want to: +- Optimize performance of existing C# backend code +- Optimize React/TypeScript frontend code +- Improve code quality and maintainability +- Reduce technical debt +- Apply best practices to existing code +- Optimize database queries and API calls +- Improve bundle size and loading performance (frontend) +- Enhance memory usage and efficiency (backend) + +## Prerequisites + +**For C# Backend:** +- .NET SDK installed (`dotnet --version`) +- Solution builds successfully +- Understanding of current code functionality + +**For React Frontend:** +- Node.js and npm installed +- Dependencies installed (`npm install`) +- Application runs without errors + +## Execution Steps + +### Step 1: Identify Code Type and Scope + +Determine what type of code needs optimization: + +**Ask user to confirm:** +- Is this C# backend code or React frontend code? +- What specific file(s) or component(s) need optimization? +- Are there specific performance issues or goals? + +**If not specified:** +- Analyze current file in editor +- Determine language/framework from file extension +- Proceed with appropriate optimization strategy + +### Step 2: Analyze Current Code + +**For C# Backend (.cs files):** + +Read and analyze the code for: + +1. **LINQ Query Optimization** + - N+1 query problems + - Inefficient `ToList()` calls + - Missing `AsNoTracking()` for read-only queries + - Complex queries that could be simplified + +2. **Async/Await Patterns** + - Missing `async/await` for I/O operations + - Blocking calls that should be async + - Unnecessary `async` keywords + +3. **Memory Management** + - Large object allocations + - String concatenation in loops + - Unnecessary object creation + - Missing `using` statements for disposables + +4. **Code Structure** + - Duplicate code + - Long methods (>50 lines) + - Complex conditional logic + - Missing abstractions + - Business logic in controllers + +5. **Database Operations** + - Inefficient queries + - Missing indexes (suggest) + - Unnecessary data loading + - Transaction management + +**For React Frontend (.tsx/.ts files):** + +Read and analyze the code for: + +1. **Component Performance** + - Unnecessary re-renders + - Missing `React.memo()` for pure components + - Missing `useMemo()` for expensive calculations + - Missing `useCallback()` for callback props + - Large components (>300 lines) + +2. **Data Fetching** + - Using `useEffect()` instead of TanStack Query + - Missing loading states + - Missing error boundaries + - No data caching strategy + - Redundant API calls + +3. **Bundle Size** + - Large dependencies + - Missing code splitting + - Missing lazy loading + - Unused imports + +4. **Code Structure** + - Duplicate components + - Complex component logic + - Missing custom hooks + - Props drilling + - Inline styles/functions + +5. **Type Safety** + - Missing TypeScript types + - `any` types usage + - Missing interface definitions + +### Step 3: Create Optimization Plan + +Based on analysis, create prioritized optimization plan: + +**Priority 1 (Critical - Performance Impact):** +- N+1 queries +- Memory leaks +- Blocking I/O operations +- Unnecessary re-renders +- Large bundle size issues + +**Priority 2 (High - Code Quality):** +- Missing async/await +- Duplicate code +- Business logic in wrong layers +- Missing error handling +- Poor type safety + +**Priority 3 (Medium - Maintainability):** +- Long methods/components +- Complex conditionals +- Missing abstractions +- Code organization + +**Present plan to user:** +- Show identified issues +- Explain priority and impact +- Ask for confirmation to proceed + +### Step 4: Apply C# Backend Optimizations + +**Optimization 1: Fix N+1 Query Problems** + +**Before:** +```csharp +var orders = await context.Orders.ToListAsync(); +foreach (var order in orders) +{ + order.Customer = await context.Customers.FindAsync(order.CustomerId); +} +``` + +**After:** +```csharp +var orders = await context.Orders + .Include(o => o.Customer) + .ToListAsync(); +``` + +**Optimization 2: Add AsNoTracking for Read-Only Queries** + +**Before:** +```csharp +public async Task> GetProductsAsync() +{ + return await context.Products.ToListAsync(); +} +``` + +**After:** +```csharp +public async Task> GetProductsAsync() +{ + return await context.Products + .AsNoTracking() + .ToListAsync(); +} +``` + +**Optimization 3: Move Business Logic from Controllers** + +**Before (Controller):** +```csharp +[HttpPost] +public async Task CreateOrder(OrderDto dto) +{ + var order = new Order { /* mapping logic */ }; + var total = 0m; + foreach (var item in dto.Items) + { + total += item.Price * item.Quantity; + } + order.Total = total; + await context.Orders.AddAsync(order); + await context.SaveChangesAsync(); + return Ok(order); +} +``` + +**After (Controller):** +```csharp +[HttpPost] +public async Task CreateOrder(CreateOrderCommand command) +{ + var result = await mediator.Send(command); + return Ok(result); +} +``` + +**After (Service/Handler):** +```csharp +public class CreateOrderCommandHandler : IRequestHandler +{ + public async Task Handle(CreateOrderCommand request, CancellationToken cancellationToken) + { + var order = request.ToEntity(); + order.CalculateTotal(); // Business logic in domain + await repository.AddAsync(order, cancellationToken); + return order.ToResult(); + } +} +``` + +**Optimization 4: Optimize String Operations** + +**Before:** +```csharp +string result = ""; +foreach (var item in items) +{ + result += item.Name + ", "; +} +``` + +**After:** +```csharp +var result = string.Join(", ", items.Select(i => i.Name)); +``` + +**Optimization 5: Improve LINQ Efficiency** + +**Before:** +```csharp +var results = await context.Orders + .ToListAsync(); +results = results.Where(o => o.Total > 100).ToList(); +``` + +**After:** +```csharp +var results = await context.Orders + .Where(o => o.Total > 100) + .ToListAsync(); +``` + +**Optimization 6: Add Caching for Expensive Operations** + +**Before:** +```csharp +public async Task> GetCategoriesAsync() +{ + return await context.Categories.ToListAsync(); +} +``` + +**After:** +```csharp +public async Task> GetCategoriesAsync() +{ + var cacheKey = "all-categories"; + if (cache.TryGetValue(cacheKey, out List categories)) + { + return categories; + } + + categories = await context.Categories + .AsNoTracking() + .ToListAsync(); + + cache.Set(cacheKey, categories, TimeSpan.FromMinutes(10)); + return categories; +} +``` + +### Step 5: Apply React Frontend Optimizations + +**Optimization 1: Replace useEffect with TanStack Query** + +**Before:** +```typescript +function ProductList() { + const [products, setProducts] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetch('/api/products') + .then(res => res.json()) + .then(data => { + setProducts(data); + setLoading(false); + }); + }, []); + + if (loading) return
Loading...
; + return
{products.map(p => )}
; +} +``` + +**After:** +```typescript +function ProductList() { + const { data: products, isLoading } = useQuery({ + queryKey: ['products'], + queryFn: () => productsService.getAll() + }); + + if (isLoading) return
Loading...
; + return
{products?.map(p => )}
; +} +``` + +**Optimization 2: Memoize Expensive Calculations** + +**Before:** +```typescript +function OrderSummary({ items }: { items: OrderItem[] }) { + const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0); + const tax = total * 0.1; + const grandTotal = total + tax; + + return
Total: ${grandTotal}
; +} +``` + +**After:** +```typescript +function OrderSummary({ items }: { items: OrderItem[] }) { + const { total, tax, grandTotal } = useMemo(() => { + const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0); + const tax = total * 0.1; + return { total, tax, grandTotal: total + tax }; + }, [items]); + + return
Total: ${grandTotal}
; +} +``` + +**Optimization 3: Memoize Components** + +**Before:** +```typescript +function ProductCard({ name, price, onAdd }: ProductCardProps) { + return ( +
+

{name}

+

${price}

+ +
+ ); +} +``` + +**After:** +```typescript +const ProductCard = React.memo(function ProductCard({ name, price, onAdd }: ProductCardProps) { + return ( +
+

{name}

+

${price}

+ +
+ ); +}); +``` + +**Optimization 4: Use useCallback for Callbacks** + +**Before:** +```typescript +function ProductList() { + const [cart, setCart] = useState([]); + + return ( +
+ {products.map(p => ( + setCart([...cart, p])} + /> + ))} +
+ ); +} +``` + +**After:** +```typescript +function ProductList() { + const [cart, setCart] = useState([]); + + const handleAdd = useCallback((product: Product) => { + setCart(prev => [...prev, product]); + }, []); + + return ( +
+ {products.map(p => ( + handleAdd(p)} + /> + ))} +
+ ); +} +``` + +**Optimization 5: Extract Custom Hooks** + +**Before:** +```typescript +function ProductList() { + const [products, setProducts] = useState([]); + const [filtered, setFiltered] = useState([]); + const [search, setSearch] = useState(''); + + useEffect(() => { + const results = products.filter(p => + p.name.toLowerCase().includes(search.toLowerCase()) + ); + setFiltered(results); + }, [products, search]); + + // render logic +} +``` + +**After:** +```typescript +function useProductFilter(products: Product[], search: string) { + return useMemo(() => + products.filter(p => + p.name.toLowerCase().includes(search.toLowerCase()) + ), + [products, search] + ); +} + +function ProductList() { + const [search, setSearch] = useState(''); + const { data: products } = useQuery(['products'], getProducts); + const filtered = useProductFilter(products ?? [], search); + + // render logic +} +``` + +**Optimization 6: Implement Code Splitting** + +**Before:** +```typescript +import { HeavyComponent } from './HeavyComponent'; + +function App() { + return ; +} +``` + +**After:** +```typescript +import { lazy, Suspense } from 'react'; + +const HeavyComponent = lazy(() => import('./HeavyComponent')); + +function App() { + return ( + Loading...}> + + + ); +} +``` + +**Optimization 7: Fix Type Safety** + +**Before:** +```typescript +function processData(data: any) { + return data.map((item: any) => item.value); +} +``` + +**After:** +```typescript +interface DataItem { + id: string; + value: number; +} + +function processData(data: DataItem[]): number[] { + return data.map(item => item.value); +} +``` + +### Step 6: Verify Optimizations + +**For C# Backend:** + +1. **Build solution:** + ```bash + dotnet build src/Managing.sln + ``` + - Ensure no compilation errors + - Check for new warnings + +2. **Run tests (if available):** + ```bash + dotnet test src/Managing.sln + ``` + - Verify all tests pass + - Check for performance improvements + +3. **Review changes:** + - Ensure business logic unchanged + - Verify API contracts maintained + - Check error handling preserved + +**For React Frontend:** + +1. **Check TypeScript:** + ```bash + npm run type-check + ``` + - Ensure no type errors + +2. **Run linter:** + ```bash + npm run lint + ``` + - Fix any new linting issues + +3. **Test component:** + ```bash + npm run test:single test/path/to/component.test.tsx + ``` + - Verify component behavior unchanged + +4. **Check bundle size:** + - Look for improvements in bundle size + - Verify lazy loading works + +5. **Manual testing:** + - Test component functionality + - Verify no regressions + - Check loading states + - Verify error handling + +### Step 7: Document Changes + +Create summary of optimizations: + +**Changes made:** +- List each optimization +- Show before/after metrics (if available) +- Explain impact of changes + +**Performance improvements:** +- Query time reductions +- Memory usage improvements +- Bundle size reductions +- Render time improvements + +**Code quality improvements:** +- Better type safety +- Reduced duplication +- Better separation of concerns +- Improved maintainability + +## Common Optimization Patterns + +### C# Backend Patterns + +1. **Repository Pattern with Specification** + - Encapsulate query logic + - Reusable query specifications + - Better testability + +2. **CQRS with MediatR** + - Separate read/write operations + - Better performance tuning + - Cleaner code organization + +3. **Caching Strategy** + - In-memory cache for frequent reads + - Distributed cache for scalability + - Cache invalidation patterns + +4. **Async Best Practices** + - Use `async/await` consistently + - Avoid `Task.Result` or `.Wait()` + - Use `ConfigureAwait(false)` in libraries + +### React Frontend Patterns + +1. **Data Fetching Pattern** + - Always use TanStack Query + - Implement proper error boundaries + - Use suspense for loading states + +2. **Component Composition** + - Split large components + - Create reusable atoms/molecules + - Use compound component pattern + +3. **State Management** + - Keep state as local as possible + - Use context sparingly + - Consider Zustand for global state + +4. **Performance Pattern** + - Memoize expensive operations + - Use React.memo for pure components + - Implement virtualization for long lists + +## Error Handling + +**If build fails after C# optimization:** +- Review changes carefully +- Check for type mismatches +- Verify async/await patterns correct +- Rollback if necessary + +**If types break after frontend optimization:** +- Check interface definitions +- Verify generic types +- Update type imports + +**If tests fail after optimization:** +- Review test expectations +- Update mocks if needed +- Verify behavior unchanged + +**If performance degrades:** +- Review optimization approach +- Check for introduced inefficiencies +- Consider alternative approach + +## Important Notes + +- ✅ **Always test after optimization** - Verify functionality unchanged +- ✅ **Measure performance** - Use profiling tools to verify improvements +- ✅ **Keep it simple** - Don't over-optimize premature code +- ✅ **Follow patterns** - Use established patterns from codebase +- ⚠️ **Avoid premature optimization** - Focus on actual bottlenecks +- ⚠️ **Maintain readability** - Don't sacrifice clarity for minor gains +- 📊 **Profile first** - Identify real performance issues before optimizing +- 🧪 **Test thoroughly** - Ensure no regressions introduced +- 📝 **Document changes** - Explain why optimizations were made + +## Example Execution + +**User input:** `/optimize-current-code` + +**AI execution:** + +1. Identify code type: React component (ProductList.tsx) +2. Analyze code: Found useEffect for data fetching, no memoization +3. Present plan: + - Replace useEffect with TanStack Query + - Add React.memo to child components + - Extract custom hooks +4. Apply optimizations (show diffs) +5. Verify: Run type-check and tests +6. Summary: "✅ Optimized ProductList component - replaced useEffect with TanStack Query, memoized child components" + +**For C# backend:** + +1. Identify code type: Service class with database operations +2. Analyze code: Found N+1 query, missing AsNoTracking, business logic +3. Present plan: + - Fix N+1 with Include + - Add AsNoTracking for read-only + - Move business logic to domain +4. Apply optimizations +5. Verify: Build and test +6. Summary: "✅ Optimized OrderService - eliminated N+1 queries, added AsNoTracking, moved business logic to domain layer" + diff --git a/src/Managing.Application/Backtests/BacktestExecutor.cs b/src/Managing.Application/Backtests/BacktestExecutor.cs index 4b5c6e64..60c59586 100644 --- a/src/Managing.Application/Backtests/BacktestExecutor.cs +++ b/src/Managing.Application/Backtests/BacktestExecutor.cs @@ -227,6 +227,7 @@ public class BacktestExecutor // Use optimized rolling window approach - TradingBox.GetSignal only needs last 600 candles const int rollingWindowSize = 600; var rollingCandles = new List(rollingWindowSize); // Pre-allocate capacity for better performance + var fixedCandlesHashSet = new HashSet(rollingWindowSize); // Reuse HashSet to avoid allocations var candlesProcessed = 0; // Signal caching optimization - reduce signal update frequency for better performance @@ -259,10 +260,14 @@ public class BacktestExecutor if (rollingCandles.Count > rollingWindowSize) { - // Remove oldest candle (first element) - O(n) but acceptable for small window + // Remove oldest candle from both structures + var removedCandle = rollingCandles[0]; rollingCandles.RemoveAt(0); + fixedCandlesHashSet.Remove(removedCandle); } + // Add to HashSet for reuse + fixedCandlesHashSet.Add(candle); tradingBot.LastCandle = candle; // Smart signal caching - reduce signal update frequency for performance @@ -276,10 +281,9 @@ public class BacktestExecutor if (!shouldSkipSignalUpdate) { - // Convert to HashSet only when needed for GetSignal (it expects HashSet) - var fixedCandles = new HashSet(rollingCandles); + // Reuse the pre-allocated HashSet instead of creating new one var signalUpdateStart = Stopwatch.GetTimestamp(); - await tradingBot.UpdateSignals(fixedCandles); + await tradingBot.UpdateSignals(fixedCandlesHashSet); signalUpdateTotalTime += Stopwatch.GetElapsedTime(signalUpdateStart); telemetry.TotalSignalUpdates++; }