feat: implement signal frequency penalties for flip-flop detection

PHASE 1 IMPLEMENTATION:
Signal quality scoring now checks database for recent trading patterns
and applies penalties to prevent overtrading and flip-flop losses.

NEW PENALTIES:
1. Overtrading: 3+ signals in 30min → -20 points
   - Detects consolidation zones where system generates excessive signals
   - Counts both executed trades AND blocked signals

2. Flip-flop: Opposite direction in last 15min → -25 points
   - Prevents rapid long→short→long whipsaws
   - Example: SHORT at 10:00, LONG at 10:12 = blocked

3. Alternating pattern: Last 3 trades flip directions → -30 points
   - Detects choppy market conditions
   - Pattern like long→short→long = system getting chopped

DATABASE INTEGRATION:
- New function: getRecentSignals() in lib/database/trades.ts
- Queries last 30min of trades + blocked signals
- Checks last 3 executed trades for alternating pattern
- Zero performance impact (fast indexed queries)

ARCHITECTURE:
- scoreSignalQuality() now async (requires database access)
- All callers updated: check-risk, execute, reentry-check
- skipFrequencyCheck flag available for special cases
- Frequency penalties included in qualityResult breakdown

EXPECTED IMPACT:
- Eliminate overnight flip-flop losses (like SOL $141-145 chop)
- Reduce overtrading during sideways consolidation
- Better capital preservation in non-trending markets
- Should improve win rate by 5-10% by avoiding worst setups

TESTING:
- Deploy and monitor next 5 signals in choppy markets
- Check logs for frequency penalty messages
- Analyze if blocked signals would have been losers

Files changed:
- lib/database/trades.ts: Added getRecentSignals()
- lib/trading/signal-quality.ts: Made async, added frequency checks
- app/api/trading/check-risk/route.ts: await + symbol parameter
- app/api/trading/execute/route.ts: await + symbol parameter
- app/api/analytics/reentry-check/route.ts: await + skipFrequencyCheck
This commit is contained in:
mindesbunister
2025-11-14 06:41:03 +01:00
parent 31bc08bed4
commit 111e3ed12a
5 changed files with 158 additions and 11 deletions

View File

@@ -36,11 +36,11 @@ export interface RiskCheckResponse {
* Position Scaling Validation
* Determines if adding to an existing position is allowed
*/
function shouldAllowScaling(
async function shouldAllowScaling(
existingTrade: ActiveTrade,
newSignal: RiskCheckRequest,
config: TradingConfig
): { allowed: boolean; reasons: string[]; qualityScore?: number; qualityReasons?: string[] } {
): Promise<{ allowed: boolean; reasons: string[]; qualityScore?: number; qualityReasons?: string[] }> {
const reasons: string[] = []
// Check if we have context metrics
@@ -50,13 +50,14 @@ function shouldAllowScaling(
}
// 1. Calculate new signal quality score
const qualityScore = scoreSignalQuality({
const qualityScore = await scoreSignalQuality({
atr: newSignal.atr,
adx: newSignal.adx,
rsi: newSignal.rsi || 50,
volumeRatio: newSignal.volumeRatio || 1,
pricePosition: newSignal.pricePosition,
direction: newSignal.direction,
symbol: newSignal.symbol,
minScore: config.minScaleQualityScore,
})
@@ -156,7 +157,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<RiskCheck
if (existingPosition.direction === body.direction) {
// Position scaling feature
if (config.enablePositionScaling) {
const scalingCheck = shouldAllowScaling(existingPosition, body, config)
const scalingCheck = await shouldAllowScaling(existingPosition, body, config)
if (scalingCheck.allowed) {
console.log('✅ Position scaling ALLOWED:', scalingCheck.reasons)
@@ -309,13 +310,14 @@ export async function POST(request: NextRequest): Promise<NextResponse<RiskCheck
// 4. Check signal quality (if context metrics provided)
if (hasContextMetrics) {
const qualityScore = scoreSignalQuality({
const qualityScore = await scoreSignalQuality({
atr: body.atr || 0,
adx: body.adx || 0,
rsi: body.rsi || 0,
volumeRatio: body.volumeRatio || 0,
pricePosition: body.pricePosition || 0,
direction: body.direction,
symbol: body.symbol,
timeframe: body.timeframe, // Pass timeframe for context-aware scoring
minScore: config.minSignalQualityScore // Use config value
})