Files
aitrade/pkg/app/strategy/defensive.go
T
kaedwen ea141eb012
Build and Push Docker Image / build-and-push (push) Failing after 1m41s
initial
2026-07-02 20:09:44 +02:00

102 lines
2.6 KiB
Go

package strategy
import (
"context"
"fmt"
"strings"
"github.com/pheinrich/aitrade/pkg/model"
)
type DefensiveStrategy struct {
stopLossEnabled bool
stopLossPercent float64
}
func NewDefensiveStrategy(stopLossEnabled bool, stopLossPercent float64) *DefensiveStrategy {
return &DefensiveStrategy{
stopLossEnabled: stopLossEnabled,
stopLossPercent: stopLossPercent,
}
}
func (s *DefensiveStrategy) Name() string {
return "defensive"
}
func (s *DefensiveStrategy) GetRiskParams() RiskParameters {
return RiskParameters{
MaxParallelTrades: 2,
MaxTradesPerHour: 3,
PositionSizePercent: 1.5, // 1-2% of capital
StopLossPercent: 2.0,
}
}
func (s *DefensiveStrategy) Analyze(ctx context.Context, market *MarketData, news []*model.NewsArticle) (*TradeSignal, error) {
// Defensive strategy: Only trade on strong positive sentiment
// with multiple confirming news articles
positiveCount := 0
negativeCount := 0
totalSentiment := 0.0
for _, article := range news {
if article.SentimentScore == nil {
continue
}
// Check if article mentions this symbol
if !strings.Contains(strings.ToUpper(article.Symbols), market.Symbol) {
continue
}
sentiment := *article.SentimentScore
totalSentiment += sentiment
if article.SentimentLabel == "positive" {
positiveCount++
} else if article.SentimentLabel == "negative" {
negativeCount++
}
}
// Defensive: Need at least 3 positive articles and no negative ones
if positiveCount < 3 || negativeCount > 0 {
return nil, nil // No trade signal
}
avgSentiment := totalSentiment / float64(len(news))
// Strong positive sentiment required (>0.5)
if avgSentiment < 0.5 {
return nil, nil
}
return &TradeSignal{
Symbol: market.Symbol,
Action: model.ActionBuy,
Quantity: 0, // Will be calculated by trader
Confidence: avgSentiment * 0.8, // Conservative confidence
Reasoning: fmt.Sprintf("Defensive: %d positive news articles, avg sentiment %.2f", positiveCount, avgSentiment),
}, nil
}
// CalculatePositionSize - Defensive strategy uses conservative sizing with confidence scaling
func (s *DefensiveStrategy) CalculatePositionSize(price float64, confidence float64, availableCapital float64) int {
basePositionPercent := s.GetRiskParams().PositionSizePercent
// Defensive: More conservative confidence scaling (0.3 - 0.8x)
confidenceMultiplier := 0.3 + (confidence * 0.5)
adjustedPercent := basePositionPercent * confidenceMultiplier
positionValue := availableCapital * (adjustedPercent / 100.0)
quantity := int(positionValue / price)
if quantity < 1 {
return 1
}
return quantity
}