- Create comprehensive PYRAMIDING_IMPLEMENTATION_PLAN.md with: - Data-driven justification (100% WR for signals ≤72 bars apart) - User-selected parameters (4h window, 7x→14x leverage) - ENV variables, database schema, execute endpoint logic - Position manager updates, Telegram notifications - 7-phase implementation checklist - 4 expected behavior examples - Add reference in copilot-instructions.md under Development Roadmap - Documents planned pyramiding system - Links to implementation plan for future agent
369 lines
11 KiB
Markdown
369 lines
11 KiB
Markdown
# Pyramiding / Signal Stacking Implementation Plan
|
||
|
||
**Created:** January 9, 2026
|
||
**Status:** 📋 PLANNED - Ready for Implementation
|
||
**Priority:** HIGH - User-requested feature based on backtested data
|
||
|
||
---
|
||
|
||
## 📊 Background & Data-Driven Justification
|
||
|
||
### Analysis Results (Jan 9, 2026)
|
||
From CSV analysis of ML v11.2 Long Only strategy on SOL/USDT 5-min chart:
|
||
|
||
| Signal Spacing | Pairs | Win Rate | Avg Combined P&L |
|
||
|----------------|-------|----------|------------------|
|
||
| ≤72 bars (6h) | 9 | **100%** | +4.82% |
|
||
| >72 bars | 34 | 67.6% | +1.94% |
|
||
|
||
**Key Finding:** Signals within 72 bars (6 hours) of each other have **100% win rate** - these represent trend confirmation and are ideal for stacking.
|
||
|
||
### User-Selected Parameters
|
||
- **Stacking Window:** 4 hours (48 bars on 5-min chart) - conservative vs 6h optimal
|
||
- **Base Leverage:** 7x (down from previous 10x for safety)
|
||
- **Stacked Leverage:** 14x total (7x + 7x)
|
||
- **Max Pyramid Levels:** 2 (base + 1 stack)
|
||
|
||
---
|
||
|
||
## 🏗️ Implementation Components
|
||
|
||
### 1. Environment Variables (.env)
|
||
|
||
```bash
|
||
# === PYRAMIDING / STACKING ===
|
||
ENABLE_PYRAMIDING=true
|
||
BASE_LEVERAGE=7
|
||
STACK_LEVERAGE=7
|
||
MAX_LEVERAGE_TOTAL=14
|
||
STACKING_WINDOW_MINUTES=240 # 4 hours = 48 bars on 5-min chart
|
||
MAX_PYRAMID_LEVELS=2 # Base + 1 additional stack
|
||
|
||
# Per-symbol overrides (optional)
|
||
SOLANA_BASE_LEVERAGE=7
|
||
SOLANA_MAX_LEVERAGE=14
|
||
SOLANA_STACKING_WINDOW_MINUTES=240
|
||
```
|
||
|
||
### 2. Configuration (config/trading.ts)
|
||
|
||
Add to `TradingConfig` interface:
|
||
```typescript
|
||
interface TradingConfig {
|
||
// ... existing fields ...
|
||
|
||
// Pyramiding settings
|
||
enablePyramiding: boolean;
|
||
baseLeverage: number;
|
||
stackLeverage: number;
|
||
maxLeverageTotal: number;
|
||
stackingWindowMinutes: number;
|
||
maxPyramidLevels: number;
|
||
}
|
||
```
|
||
|
||
Add to `DEFAULT_TRADING_CONFIG`:
|
||
```typescript
|
||
enablePyramiding: true,
|
||
baseLeverage: 7,
|
||
stackLeverage: 7,
|
||
maxLeverageTotal: 14,
|
||
stackingWindowMinutes: 240, // 4 hours
|
||
maxPyramidLevels: 2,
|
||
```
|
||
|
||
Add to `getConfigFromEnv()`:
|
||
```typescript
|
||
enablePyramiding: process.env.ENABLE_PYRAMIDING === 'true',
|
||
baseLeverage: parseInt(process.env.BASE_LEVERAGE || '7'),
|
||
stackLeverage: parseInt(process.env.STACK_LEVERAGE || '7'),
|
||
maxLeverageTotal: parseInt(process.env.MAX_LEVERAGE_TOTAL || '14'),
|
||
stackingWindowMinutes: parseInt(process.env.STACKING_WINDOW_MINUTES || '240'),
|
||
maxPyramidLevels: parseInt(process.env.MAX_PYRAMID_LEVELS || '2'),
|
||
```
|
||
|
||
### 3. Database Schema (prisma/schema.prisma)
|
||
|
||
Add to Trade model:
|
||
```prisma
|
||
model Trade {
|
||
// ... existing fields ...
|
||
|
||
// Pyramiding tracking
|
||
pyramidLevel Int? @default(1) // 1 = base, 2 = first stack, etc.
|
||
parentTradeId String? // Reference to base trade if stacked
|
||
stackedAt DateTime? // When stack was added
|
||
totalLeverageAtEntry Float? // Total leverage including stacks
|
||
isStackedPosition Boolean @default(false) // True if this is a stack entry
|
||
}
|
||
```
|
||
|
||
### 4. Execute Endpoint (app/api/trading/execute/route.ts)
|
||
|
||
#### A. Add Stacking Logic Check
|
||
|
||
Before opening a new position, check if we should stack:
|
||
|
||
```typescript
|
||
// Check for existing open position in same direction
|
||
const existingOpenTrade = await prisma.trade.findFirst({
|
||
where: {
|
||
symbol: driftSymbol,
|
||
direction: direction,
|
||
exitReason: null, // Still open
|
||
},
|
||
orderBy: { createdAt: 'desc' }
|
||
});
|
||
|
||
// Determine if this is a stack opportunity
|
||
let isStackEntry = false;
|
||
let effectiveLeverage = config.baseLeverage;
|
||
let pyramidLevel = 1;
|
||
|
||
if (existingOpenTrade && config.enablePyramiding) {
|
||
const minutesSinceEntry = (Date.now() - new Date(existingOpenTrade.createdAt).getTime()) / 60000;
|
||
const currentPyramidLevel = existingOpenTrade.pyramidLevel || 1;
|
||
|
||
if (minutesSinceEntry <= config.stackingWindowMinutes &&
|
||
currentPyramidLevel < config.maxPyramidLevels) {
|
||
// This is a valid stack opportunity
|
||
isStackEntry = true;
|
||
pyramidLevel = currentPyramidLevel + 1;
|
||
effectiveLeverage = config.stackLeverage; // Add another 7x
|
||
|
||
console.log(`📈 PYRAMID STACK: Signal within ${minutesSinceEntry.toFixed(0)} minutes`)
|
||
console.log(` Level: ${pyramidLevel}/${config.maxPyramidLevels}`)
|
||
console.log(` Adding: ${effectiveLeverage}x leverage`)
|
||
console.log(` Total: ${currentPyramidLevel * config.baseLeverage + effectiveLeverage}x`)
|
||
} else if (minutesSinceEntry > config.stackingWindowMinutes) {
|
||
console.log(`⏰ Stack window expired: ${minutesSinceEntry.toFixed(0)} min > ${config.stackingWindowMinutes} min`)
|
||
console.log(` Treating as independent signal (not stacking)`)
|
||
} else if (currentPyramidLevel >= config.maxPyramidLevels) {
|
||
console.log(`🔒 Max pyramid level reached: ${currentPyramidLevel}/${config.maxPyramidLevels}`)
|
||
console.log(` Ignoring additional signal`)
|
||
return NextResponse.json({
|
||
success: false,
|
||
error: 'Max pyramid level reached',
|
||
details: { currentLevel: currentPyramidLevel, maxLevel: config.maxPyramidLevels }
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
#### B. Modify Position Opening
|
||
|
||
When opening position, use the determined leverage:
|
||
|
||
```typescript
|
||
// Use effective leverage (base or stack)
|
||
const positionParams = {
|
||
symbol: driftSymbol,
|
||
direction: direction,
|
||
leverage: effectiveLeverage, // 7x for both base and stack
|
||
// ... other params
|
||
};
|
||
```
|
||
|
||
#### C. Update Database Record
|
||
|
||
Store pyramiding info in trade record:
|
||
|
||
```typescript
|
||
const tradeData = {
|
||
// ... existing fields ...
|
||
pyramidLevel: pyramidLevel,
|
||
parentTradeId: isStackEntry ? existingOpenTrade.id : null,
|
||
stackedAt: isStackEntry ? new Date() : null,
|
||
totalLeverageAtEntry: isStackEntry
|
||
? (existingOpenTrade.totalLeverageAtEntry || config.baseLeverage) + effectiveLeverage
|
||
: effectiveLeverage,
|
||
isStackedPosition: isStackEntry,
|
||
};
|
||
```
|
||
|
||
### 5. Position Manager (lib/trading/position-manager.ts)
|
||
|
||
#### A. Track Stacked Positions Together
|
||
|
||
Modify `ActiveTrade` interface:
|
||
```typescript
|
||
interface ActiveTrade {
|
||
// ... existing fields ...
|
||
pyramidLevel: number;
|
||
parentTradeId?: string;
|
||
isStackedPosition: boolean;
|
||
totalLeverage: number;
|
||
}
|
||
```
|
||
|
||
#### B. Unified Exit Logic
|
||
|
||
When closing a position, close ALL pyramid levels:
|
||
```typescript
|
||
async closePosition(tradeId: string, reason: string) {
|
||
const trade = this.activeTrades.get(tradeId);
|
||
|
||
// If this is a base position with stacks, close all
|
||
if (trade && !trade.isStackedPosition) {
|
||
const stackedTrades = Array.from(this.activeTrades.values())
|
||
.filter(t => t.parentTradeId === tradeId);
|
||
|
||
for (const stackedTrade of stackedTrades) {
|
||
console.log(`🔗 Closing stacked position: ${stackedTrade.id} (level ${stackedTrade.pyramidLevel})`);
|
||
await this.executeClose(stackedTrade, reason);
|
||
}
|
||
}
|
||
|
||
await this.executeClose(trade, reason);
|
||
}
|
||
```
|
||
|
||
### 6. Telegram Notifications (lib/notifications/telegram.ts)
|
||
|
||
Update position notifications to show pyramid info:
|
||
|
||
```typescript
|
||
// For stacked entries
|
||
if (trade.isStackedPosition) {
|
||
message += `\n📈 PYRAMID STACK (Level ${trade.pyramidLevel}/${config.maxPyramidLevels})`;
|
||
message += `\n💪 Total Leverage: ${trade.totalLeverageAtEntry}x`;
|
||
message += `\n⏱️ Stacked ${Math.round((Date.now() - new Date(trade.parentTrade.createdAt).getTime()) / 60000)} min after base`;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔒 Risk Management Safeguards
|
||
|
||
### Critical Safety Checks
|
||
|
||
1. **Max Leverage Guard**
|
||
```typescript
|
||
if (totalLeverage > config.maxLeverageTotal) {
|
||
console.error(`❌ BLOCKED: Would exceed max leverage (${totalLeverage}x > ${config.maxLeverageTotal}x)`);
|
||
return;
|
||
}
|
||
```
|
||
|
||
2. **Same Direction Only**
|
||
- Only stack in same direction as existing position
|
||
- Opposite signal should close existing position, not stack
|
||
|
||
3. **Time Window Enforcement**
|
||
- Hard 4-hour (240 minute) window
|
||
- Signals outside window treated as independent trades
|
||
|
||
4. **Max Levels Cap**
|
||
- Maximum 2 pyramid levels (base + 1 stack = 14x max)
|
||
- Additional signals beyond max are ignored with warning
|
||
|
||
5. **Quality Score Requirement**
|
||
- Stacked entries should still pass quality score threshold
|
||
- Don't stack low-quality confirmations
|
||
|
||
---
|
||
|
||
## 📋 Implementation Checklist
|
||
|
||
### Phase 1: Configuration
|
||
- [ ] Add ENV variables to `.env`
|
||
- [ ] Update `TradingConfig` interface in `config/trading.ts`
|
||
- [ ] Add defaults to `DEFAULT_TRADING_CONFIG`
|
||
- [ ] Add ENV parsing to `getConfigFromEnv()`
|
||
|
||
### Phase 2: Database
|
||
- [ ] Add new fields to Trade model in `prisma/schema.prisma`
|
||
- [ ] Run `npx prisma migrate dev --name add_pyramiding_fields`
|
||
- [ ] Run `npx prisma generate`
|
||
|
||
### Phase 3: Execute Endpoint
|
||
- [ ] Add existing position check logic
|
||
- [ ] Add pyramid level calculation
|
||
- [ ] Add stacking window validation
|
||
- [ ] Modify position opening with effective leverage
|
||
- [ ] Update trade record with pyramid info
|
||
- [ ] Add logging for stack events
|
||
|
||
### Phase 4: Position Manager
|
||
- [ ] Update ActiveTrade interface
|
||
- [ ] Add stack tracking to position Map
|
||
- [ ] Implement unified exit (close all levels together)
|
||
- [ ] Update MAE/MFE tracking for combined position
|
||
|
||
### Phase 5: Notifications
|
||
- [ ] Update Telegram entry notification with stack info
|
||
- [ ] Update exit notification with combined P&L
|
||
|
||
### Phase 6: Testing
|
||
- [ ] Test base entry (7x leverage)
|
||
- [ ] Test stack entry within window (14x total)
|
||
- [ ] Test signal outside window (independent trade)
|
||
- [ ] Test max pyramid level blocking
|
||
- [ ] Test unified exit closes all levels
|
||
- [ ] Test opposite direction closes stacked position
|
||
|
||
### Phase 7: Documentation
|
||
- [ ] Update copilot-instructions.md with pyramiding section
|
||
- [ ] Add to Settings UI (optional)
|
||
|
||
---
|
||
|
||
## 📊 Expected Behavior Examples
|
||
|
||
### Example 1: Successful Stack
|
||
```
|
||
10:00 - BUY signal (quality 95) → Open LONG 7x leverage
|
||
12:30 - BUY signal (quality 92) → Stack! Within 4h window
|
||
→ Add 7x leverage
|
||
→ Total position: 14x leverage
|
||
14:00 - TP1 hit → Close entire stacked position
|
||
→ P&L calculated on 14x total leverage
|
||
```
|
||
|
||
### Example 2: Outside Window
|
||
```
|
||
10:00 - BUY signal → Open LONG 7x leverage
|
||
15:00 - BUY signal → 5 hours later, outside 4h window
|
||
→ Treated as independent trade
|
||
→ Close existing position, open new 7x position
|
||
```
|
||
|
||
### Example 3: Max Level Reached
|
||
```
|
||
10:00 - BUY signal → Open LONG 7x (level 1)
|
||
11:00 - BUY signal → Stack 7x (level 2, total 14x)
|
||
12:00 - BUY signal → Max level reached!
|
||
→ Log warning, ignore signal
|
||
→ Keep existing 14x position
|
||
```
|
||
|
||
### Example 4: Opposite Signal
|
||
```
|
||
10:00 - BUY signal → Open LONG 7x leverage
|
||
11:00 - SELL signal → Opposite direction!
|
||
→ Close LONG position entirely
|
||
→ Open new SHORT 7x position
|
||
```
|
||
|
||
---
|
||
|
||
## 🔗 References
|
||
|
||
- **Analysis Source:** `/home/icke/traderv4/docs/PYRAMIDING_IMPLEMENTATION_PLAN.md`
|
||
- **TradingView Strategy:** `/home/icke/traderv4/workflows/trading/moneyline_v11_2_strategy.pinescript`
|
||
- **CSV Analysis Data:** `ML_v11.2_Strat_MEXC_SOLUSDT.P_2026-01-09.csv`
|
||
- **Optimal Threshold Analysis:** 72 bars (6 hours) = 100% WR for stacked signals
|
||
|
||
---
|
||
|
||
## ⚠️ Important Notes
|
||
|
||
1. **Conservative Window:** User chose 4 hours (48 bars) vs optimal 6 hours (72 bars) for safety
|
||
2. **Leverage Reduction:** Base leverage reduced from 10x to 7x to accommodate stacking safely
|
||
3. **Total Cap:** 14x maximum (7x + 7x) prevents over-leveraging
|
||
4. **Quality Gate:** Stack signals should still meet quality thresholds
|
||
|
||
---
|
||
|
||
*This plan is ready for implementation. Reference this document when implementing pyramiding in the trading bot.*
|