115 lines
2.9 KiB
Go
115 lines
2.9 KiB
Go
package strategy
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/pheinrich/aitrade/pkg/model"
|
|
)
|
|
|
|
type AggressiveStrategy struct {
|
|
stopLossEnabled bool
|
|
stopLossPercent float64
|
|
}
|
|
|
|
func NewAggressiveStrategy(stopLossEnabled bool, stopLossPercent float64) *AggressiveStrategy {
|
|
return &AggressiveStrategy{
|
|
stopLossEnabled: stopLossEnabled,
|
|
stopLossPercent: stopLossPercent,
|
|
}
|
|
}
|
|
|
|
func (s *AggressiveStrategy) Name() string {
|
|
return "aggressive"
|
|
}
|
|
|
|
func (s *AggressiveStrategy) GetRiskParams() RiskParameters {
|
|
return RiskParameters{
|
|
MaxParallelTrades: 10,
|
|
MaxTradesPerHour: 12,
|
|
PositionSizePercent: 7.5, // 5-10% of capital
|
|
StopLossPercent: 5.0,
|
|
}
|
|
}
|
|
|
|
func (s *AggressiveStrategy) Analyze(ctx context.Context, market *MarketData, news []*model.NewsArticle) (*TradeSignal, error) {
|
|
// Aggressive strategy: Trade on any positive sentiment
|
|
// Higher risk, higher frequency
|
|
|
|
positiveCount := 0
|
|
negativeCount := 0
|
|
totalSentiment := 0.0
|
|
relevantArticles := 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
|
|
}
|
|
|
|
relevantArticles++
|
|
sentiment := *article.SentimentScore
|
|
totalSentiment += sentiment
|
|
|
|
if article.SentimentLabel == "positive" {
|
|
positiveCount++
|
|
} else if article.SentimentLabel == "negative" {
|
|
negativeCount++
|
|
}
|
|
}
|
|
|
|
// Aggressive: Need at least 1 relevant article
|
|
if relevantArticles < 1 {
|
|
return nil, nil
|
|
}
|
|
|
|
avgSentiment := totalSentiment / float64(relevantArticles)
|
|
|
|
// Aggressive: Any net positive sentiment
|
|
if avgSentiment <= 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
// Also consider sell signals on strong negative sentiment
|
|
if avgSentiment < -0.5 && negativeCount > positiveCount {
|
|
return &TradeSignal{
|
|
Symbol: market.Symbol,
|
|
Action: model.ActionSell,
|
|
Quantity: 0, // Will be filled from position
|
|
Confidence: (-avgSentiment) * 0.95,
|
|
Reasoning: fmt.Sprintf("Aggressive SELL: %d negative articles, avg sentiment %.2f", negativeCount, avgSentiment),
|
|
}, nil
|
|
}
|
|
|
|
return &TradeSignal{
|
|
Symbol: market.Symbol,
|
|
Action: model.ActionBuy,
|
|
Quantity: 0, // Will be calculated by trader
|
|
Confidence: avgSentiment * 0.95,
|
|
Reasoning: fmt.Sprintf("Aggressive BUY: %d positive articles, avg sentiment %.2f", positiveCount, avgSentiment),
|
|
}, nil
|
|
}
|
|
|
|
// CalculatePositionSize - Aggressive strategy uses larger sizing with confidence scaling
|
|
func (s *AggressiveStrategy) CalculatePositionSize(price float64, confidence float64, availableCapital float64) int {
|
|
basePositionPercent := s.GetRiskParams().PositionSizePercent
|
|
|
|
// Aggressive: More aggressive confidence scaling (0.7 - 1.2x)
|
|
confidenceMultiplier := 0.7 + (confidence * 0.5)
|
|
adjustedPercent := basePositionPercent * confidenceMultiplier
|
|
|
|
positionValue := availableCapital * (adjustedPercent / 100.0)
|
|
quantity := int(positionValue / price)
|
|
|
|
if quantity < 1 {
|
|
return 1
|
|
}
|
|
|
|
return quantity
|
|
}
|