prabalGaur commited on
Commit
d57bce9
·
verified ·
1 Parent(s): cf73b4f

Upload community_contributions/chrys/agents/tech_analyst.py with huggingface_hub

Browse files
community_contributions/chrys/agents/tech_analyst.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """TECH_ANALYST agent: technical indicators and signal scoring."""
2
+ from typing import List
3
+
4
+ from agents.base import AgentBase
5
+ from models import AssetData, TechResult
6
+
7
+
8
+ def _rsi(closes: List[float], period: int = 14) -> float:
9
+ if len(closes) < period + 1:
10
+ return 50.0
11
+ import pandas as pd
12
+ s = pd.Series(closes)
13
+ delta = s.diff()
14
+ gain = delta.where(delta > 0, 0.0)
15
+ loss = (-delta).where(delta < 0, 0.0)
16
+ avg_gain = gain.rolling(period).mean()
17
+ avg_loss = loss.rolling(period).mean()
18
+ rs = avg_gain / avg_loss.replace(0, 1e-10)
19
+ rsi = 100 - (100 / (1 + rs))
20
+ return float(rsi.iloc[-1]) if not pd.isna(rsi.iloc[-1]) else 50.0
21
+
22
+
23
+ def _ema(series: List[float], period: int) -> List[float]:
24
+ if not series:
25
+ return []
26
+ import pandas as pd
27
+ s = pd.Series(series)
28
+ ema = s.ewm(span=period, adjust=False).mean()
29
+ return ema.tolist()
30
+
31
+
32
+ def _macd_signal(closes: List[float], fast: int = 12, slow: int = 26, signal: int = 9) -> str:
33
+ if len(closes) < slow + signal:
34
+ return "NEUTRAL"
35
+ ema_f = _ema(closes, fast)
36
+ ema_s = _ema(closes, slow)
37
+ macd_line = [ema_f[i] - ema_s[i] for i in range(len(ema_f))]
38
+ if len(macd_line) < signal:
39
+ return "NEUTRAL"
40
+ signal_line = _ema(macd_line, signal)
41
+ if len(signal_line) < 2:
42
+ return "NEUTRAL"
43
+ if macd_line[-1] > signal_line[-1] and macd_line[-2] <= signal_line[-2]:
44
+ return "BULLISH_CROSS"
45
+ if macd_line[-1] < signal_line[-1] and macd_line[-2] >= signal_line[-2]:
46
+ return "BEARISH_CROSS"
47
+ return "NEUTRAL"
48
+
49
+
50
+ def _bb_signal(closes: List[float], period: int = 20, k: float = 2.0) -> str:
51
+ if len(closes) < period:
52
+ return "NEUTRAL"
53
+ import pandas as pd
54
+ s = pd.Series(closes)
55
+ ma = s.rolling(period).mean().iloc[-1]
56
+ std = s.rolling(period).std().iloc[-1]
57
+ if pd.isna(std) or std == 0:
58
+ return "NEUTRAL"
59
+ upper = ma + k * std
60
+ lower = ma - k * std
61
+ price = closes[-1]
62
+ if price <= lower:
63
+ return "LOWER_BAND_BOUNCE"
64
+ if price >= upper:
65
+ return "UPPER_BAND_TOUCH"
66
+ return "NEUTRAL"
67
+
68
+
69
+ def _ema_cross(closes: List[float], fast: int = 9, slow: int = 21) -> str:
70
+ if len(closes) < slow + 1:
71
+ return "NEUTRAL"
72
+ ema_f = _ema(closes, fast)
73
+ ema_s = _ema(closes, slow)
74
+ if ema_f[-1] > ema_s[-1] and ema_f[-2] <= ema_s[-2]:
75
+ return "GOLDEN_CROSS"
76
+ if ema_f[-1] < ema_s[-1] and ema_f[-2] >= ema_s[-2]:
77
+ return "DEATH_CROSS"
78
+ return "NEUTRAL"
79
+
80
+
81
+ class TechAnalystAgent(AgentBase):
82
+ name = "TECH_ANALYST"
83
+ logger_name = "aria.tech_analyst"
84
+
85
+ def run(self, assets: List[AssetData]) -> List[TechResult]:
86
+ self.log("Running technical analysis")
87
+ results: List[TechResult] = []
88
+ for a in assets:
89
+ closes = [c["c"] for c in (a.ohlcv_50 or a.ohlcv_14) if isinstance(c, dict) and "c" in c]
90
+ if not closes:
91
+ closes = [a.price]
92
+ rsi = _rsi(closes)
93
+ macd_signal = _macd_signal(closes)
94
+ bb_signal = _bb_signal(closes)
95
+ ema_cross = _ema_cross(closes)
96
+ volume_spike = a.volume_ratio > 1.5
97
+ score = 0
98
+ # Bullish
99
+ if rsi < 30:
100
+ score += 20
101
+ elif rsi > 70:
102
+ score -= 20
103
+ if macd_signal == "BULLISH_CROSS":
104
+ score += 25
105
+ elif macd_signal == "BEARISH_CROSS":
106
+ score -= 25
107
+ if bb_signal == "LOWER_BAND_BOUNCE":
108
+ score += 20
109
+ elif bb_signal == "UPPER_BAND_TOUCH":
110
+ score -= 20
111
+ if ema_cross == "GOLDEN_CROSS":
112
+ score += 25
113
+ elif ema_cross == "DEATH_CROSS":
114
+ score -= 25
115
+ if volume_spike:
116
+ score += 10
117
+ if a.high_52w and a.price >= a.high_52w * 0.97:
118
+ score += 10
119
+ elif a.low_52w and a.price <= a.low_52w * 1.05:
120
+ score += 15
121
+ score = max(-100, min(100, score))
122
+ if score >= 50:
123
+ bias = "STRONG BUY"
124
+ elif score >= 20:
125
+ bias = "MODERATE BUY"
126
+ elif score <= -50:
127
+ bias = "STRONG SELL"
128
+ elif score <= -20:
129
+ bias = "MODERATE SELL"
130
+ else:
131
+ bias = "NEUTRAL"
132
+ results.append(TechResult(
133
+ asset=a.asset,
134
+ rsi=round(rsi, 1),
135
+ macd_signal=macd_signal,
136
+ bb_signal=bb_signal,
137
+ ema_cross=ema_cross,
138
+ volume_spike=volume_spike,
139
+ tech_score=score,
140
+ bias=bias,
141
+ ))
142
+ self.log(f"{a.asset}: score={score} bias={bias}")
143
+ self.log(f"Tech analysis complete: {len(results)} assets")
144
+ return results