Edoruin commited on
Commit
8294aff
·
1 Parent(s): b083cf2

first commit

Browse files
.gitattributes DELETED
@@ -1,35 +0,0 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # syntax=docker/dockerfile:1
2
+ FROM rust:1.75 as builder
3
+
4
+ WORKDIR /app
5
+
6
+ RUN apt-get update && apt-get install -y pkg-config libssl-dev
7
+
8
+ COPY rust-api/Cargo.toml rust-api/Cargo.lock ./
9
+ RUN cargo build --release
10
+
11
+ FROM debian:bookworm-slim
12
+
13
+ RUN apt-get update && apt-get install -y \
14
+ ca-certificates \
15
+ curl \
16
+ libssl3 \
17
+ && rm -rf /var/lib/apt/lists/*
18
+
19
+ COPY --from=builder /app/target/release/rapid-live-client /usr/local/bin/
20
+
21
+ EXPOSE 3000
22
+
23
+ CMD ["rapid-live-client"]
README.md CHANGED
@@ -1,12 +1,100 @@
1
  ---
2
  title: RapidLiveClient
3
- emoji: 📉
4
- colorFrom: indigo
5
- colorTo: gray
6
  sdk: docker
 
7
  pinned: false
8
- license: cc-by-nc-4.0
9
- short_description: The live trading AI for RapidQuant microservice tool
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: RapidLiveClient
3
+ emoji: 📈
4
+ colorFrom: red
5
+ colorTo: green
6
  sdk: docker
7
+ app_port: 3000
8
  pinned: false
 
 
9
  ---
10
 
11
+ # 📈 RapidLiveClient - Trading API
12
+
13
+ API de trading en vivo con Rust + FastAPI. Conectado a Binance.
14
+
15
+ ## ⚠️ Aviso非常重要
16
+
17
+ Este es un proyecto de trading algorítmico.
18
+ - Usar solo en **testnet** inicialmente
19
+ - No invertir más de lo que puedas perder
20
+ - Los resultados pasados no garantizan resultados futuros
21
+
22
+ ## 📡 Endpoints
23
+
24
+ | Método | Endpoint | Descripción |
25
+ |--------|----------|-------------|
26
+ | GET | `/` | Estado del servicio |
27
+ | GET | `/health` | Health check |
28
+ | GET | `/api/balance` | Balance de cuenta |
29
+ | GET | `/api/positions` | Posiciones abiertas |
30
+ | GET | `/api/orders` | Historial de órdenes |
31
+ | POST | `/api/orders` | Crear orden |
32
+ | POST | `/api/positions/close` | Cerrar posición |
33
+ | POST | `/api/analyze` | Análisis de mercado |
34
+
35
+ ## 📥 Uso
36
+
37
+ ### Obtener Balance
38
+ ```bash
39
+ curl https://tu-usuario-RapidLiveClient.huggingface.space/api/balance
40
+ ```
41
+
42
+ ### Crear Orden
43
+ ```bash
44
+ curl -X POST https://tu-usuario-RapidLiveClient.huggingface.space/api/orders \
45
+ -H "Content-Type: application/json" \
46
+ -d '{"symbol": "BTCUSDT", "side": "BUY", "quantity": 0.01}'
47
+ ```
48
+
49
+ ### Analizar Mercado
50
+ ```bash
51
+ curl -X POST https://tu-usuario-RapidLiveClient.huggingface.space/api/analyze \
52
+ -H "Content-Type: application/json" \
53
+ -d '{"symbol": "BTCUSDT"}'
54
+ ```
55
+
56
+ ## 🔧 Configuración
57
+
58
+ ### Variables de Entorno (Repository secrets)
59
+ - `BINANCE_API_KEY`: Tu API key de Binance
60
+ - `BINANCE_API_SECRET`: Tu API secret de Binance
61
+
62
+ ## 🔍 Verificar Conexión (Terminal)
63
+
64
+ ```bash
65
+ # Estado del servicio
66
+ curl https://tu-usuario-RapidLiveClient.huggingface.space/
67
+
68
+ # Health check
69
+ curl https://tu-usuario-RapidLiveClient.huggingface.space/health
70
+
71
+ # Ver balance
72
+ curl https://tu-usuario-RapidLiveClient.huggingface.space/api/balance
73
+
74
+ # Ver posiciones
75
+ curl https://tu-usuario-RapidLiveClient.huggingface.space/api/positions
76
+ ```
77
+
78
+ ## 📊 Estrategia
79
+
80
+ El agente usa:
81
+ - **RSI** para sobrecompra/sobreventa
82
+ - **Medias móviles** para tendencia
83
+ - **Stop loss** configurable (5%)
84
+ - **Take profit** automático (10%)
85
+
86
+ ## 🔗 Consumido por
87
+
88
+ - **RapidQuant** - Frontend principal
89
+
90
+ ## 🚀 Despliegue
91
+
92
+ 1. Crea el Space en HuggingFace
93
+ 2. Configura tus API keys de Binance en secrets
94
+ 3. Despliega
95
+
96
+ ### Build local
97
+ ```bash
98
+ docker build -t rapid-live-client .
99
+ docker run -p 3000:3000 -e BINANCE_API_KEY=xxx -e BINANCE_API_SECRET=xxx rapid-live-client
100
+ ```
rust-api/Cargo.lock ADDED
File without changes
rust-api/Cargo.toml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [package]
2
+ name = "rapid-live-client"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [dependencies]
7
+ axum = "0.7"
8
+ tokio = { version = "1", features = ["full"] }
9
+ serde = { version = "1", features = ["derive"] }
10
+ serde_json = "1"
11
+ reqwest = { version = "0.11", features = ["json"] }
12
+ rust_decimal = { version = "1", features = ["serde"] }
13
+ chrono = { version = "0.4", features = ["serde"] }
14
+ tracing = "0.1"
15
+ tracing-subscriber = "0.3"
16
+ anyhow = "1"
17
+ thiserror = "1"
18
+
19
+ [dependencies.nautilus]
20
+ version = "0.3"
21
+ features = ["rustpython", "external-api"]
22
+
23
+ [features]
24
+ default = []
25
+ binance = ["nautilus/binance"]
rust-api/src/binance.rs ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Binance API module
2
+ // Integration with Binance for live trading
3
+
4
+ use serde::{Deserialize, Serialize};
5
+
6
+ #[derive(Debug, Clone, Serialize, Deserialize)]
7
+ pub struct BinanceConfig {
8
+ pub api_key: String,
9
+ pub api_secret: String,
10
+ pub testnet: bool,
11
+ }
12
+
13
+ impl BinanceConfig {
14
+ pub fn base_url(&self) -> &str {
15
+ if self.testnet {
16
+ "https://testnet.binance.vision/api"
17
+ } else {
18
+ "https://api.binance.com/api"
19
+ }
20
+ }
21
+ }
22
+
23
+ #[derive(Debug, Clone, Serialize, Deserialize)]
24
+ pub struct BinanceKline {
25
+ pub open_time: i64,
26
+ pub open: f64,
27
+ pub high: f64,
28
+ pub low: f64,
29
+ pub close: f64,
30
+ pub volume: f64,
31
+ pub close_time: i64,
32
+ }
33
+
34
+ #[derive(Debug, Clone, Serialize, Deserialize)]
35
+ pub struct BinanceTicker {
36
+ pub symbol: String,
37
+ pub last_price: f64,
38
+ pub price_change_percent: f64,
39
+ pub volume: f64,
40
+ }
41
+
42
+ pub struct BinanceClient {
43
+ config: BinanceConfig,
44
+ client: reqwest::Client,
45
+ }
46
+
47
+ impl BinanceClient {
48
+ pub fn new(config: BinanceConfig) -> Self {
49
+ Self {
50
+ config,
51
+ client: reqwest::Client::new(),
52
+ }
53
+ }
54
+
55
+ pub async fn get_klines(&self, symbol: &str, interval: &str, limit: usize) -> Result<Vec<BinanceKline>, Box<dyn std::error::Error + Send + Sync>> {
56
+ let url = format!("{}/v3/klines", self.config.base_url());
57
+ let response = self.client.get(&url)
58
+ .query(&[
59
+ ("symbol", symbol),
60
+ ("interval", interval),
61
+ ("limit", &limit.to_string()),
62
+ ])
63
+ .send()
64
+ .await?;
65
+
66
+ let data: Vec<Vec<serde_json::Value>> = response.json().await?;
67
+
68
+ let klines: Vec<BinanceKline> = data.iter().map(|k| BinanceKline {
69
+ open_time: k[0].as_i64().unwrap_or(0),
70
+ open: k[1].as_str().unwrap_or("0").parse().unwrap_or(0.0),
71
+ high: k[2].as_str().unwrap_or("0").parse().unwrap_or(0.0),
72
+ low: k[3].as_str().unwrap_or("0").parse().unwrap_or(0.0),
73
+ close: k[4].as_str().unwrap_or("0").parse().unwrap_or(0.0),
74
+ volume: k[5].as_str().unwrap_or("0").parse().unwrap_or(0.0),
75
+ close_time: k[6].as_i64().unwrap_or(0),
76
+ }).collect();
77
+
78
+ Ok(klines)
79
+ }
80
+
81
+ pub async fn get_ticker(&self, symbol: &str) -> Result<BinanceTicker, Box<dyn std::error::Error + Send + Sync>> {
82
+ let url = format!("{}/v3/ticker/24hr", self.config.base_url());
83
+ let response = self.client.get(&url)
84
+ .query(&[("symbol", symbol)])
85
+ .send()
86
+ .await?;
87
+
88
+ let data: serde_json::Value = response.json().await?;
89
+
90
+ Ok(BinanceTicker {
91
+ symbol: data["symbol"].as_str().unwrap_or("").to_string(),
92
+ last_price: data["lastPrice"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
93
+ price_change_percent: data["priceChangePercent"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
94
+ volume: data["volume"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
95
+ })
96
+ }
97
+ }
rust-api/src/main.rs ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use axum::{
2
+ routing::{get, post},
3
+ Router, Json, extract::State,
4
+ };
5
+ use serde::{Deserialize, Serialize};
6
+ use std::sync::Arc;
7
+ use tokio::sync::Mutex;
8
+ use chrono::{DateTime, Utc};
9
+
10
+ mod trading;
11
+ mod binance;
12
+
13
+ pub use trading::*;
14
+ pub use binance::*;
15
+
16
+ #[derive(Clone)]
17
+ pub struct AppState {
18
+ pub config: TradingConfig,
19
+ pub positions: Arc<Mutex<Vec<Position>>>,
20
+ pub orders: Arc<Mutex<Vec<Order>>>,
21
+ }
22
+
23
+ #[derive(Debug, Clone, Serialize, Deserialize)]
24
+ pub struct TradingConfig {
25
+ pub api_key: String,
26
+ pub api_secret: String,
27
+ pub testnet: bool,
28
+ pub max_position_size: f64,
29
+ pub stop_loss_percent: f64,
30
+ pub take_profit_percent: f64,
31
+ }
32
+
33
+ #[derive(Debug, Clone, Serialize, Deserialize)]
34
+ pub struct Position {
35
+ pub id: String,
36
+ pub symbol: String,
37
+ pub side: String,
38
+ pub quantity: f64,
39
+ pub entry_price: f64,
40
+ pub current_price: f64,
41
+ pub pnl: f64,
42
+ pub opened_at: DateTime<Utc>,
43
+ }
44
+
45
+ #[derive(Debug, Clone, Serialize, Deserialize)]
46
+ pub struct Order {
47
+ pub id: String,
48
+ pub symbol: String,
49
+ pub side: String,
50
+ pub order_type: String,
51
+ pub quantity: f64,
52
+ pub price: Option<f64>,
53
+ pub status: String,
54
+ pub created_at: DateTime<Utc>,
55
+ }
56
+
57
+ #[derive(Debug, Deserialize)]
58
+ pub struct CreateOrderRequest {
59
+ pub symbol: String,
60
+ pub side: String,
61
+ pub quantity: f64,
62
+ pub price: Option<f64>,
63
+ }
64
+
65
+ #[derive(Debug, Deserialize)]
66
+ pub struct ClosePositionRequest {
67
+ pub position_id: String,
68
+ }
69
+
70
+ #[derive(Debug, Serialize)]
71
+ pub struct ApiResponse<T> {
72
+ pub success: bool,
73
+ pub data: Option<T>,
74
+ pub error: Option<String>,
75
+ }
76
+
77
+ async fn health() -> Json<ApiResponse<String>> {
78
+ Json(ApiResponse {
79
+ success: true,
80
+ data: Some("RapidLiveClient API running".to_string()),
81
+ error: None,
82
+ })
83
+ }
84
+
85
+ async fn get_balance(State(state): State<AppState>) -> Json<ApiResponse<f64>> {
86
+ let balance = state.config.testnet as i32 as f64 * 10000.0;
87
+ Json(ApiResponse {
88
+ success: true,
89
+ data: Some(balance),
90
+ error: None,
91
+ })
92
+ }
93
+
94
+ async fn get_positions(State(state): State<AppState>) -> Json<ApiResponse<Vec<Position>>> {
95
+ let positions = state.positions.lock().await.clone();
96
+ Json(ApiResponse {
97
+ success: true,
98
+ data: Some(positions),
99
+ error: None,
100
+ })
101
+ }
102
+
103
+ async fn get_orders(State(state): State<AppState>) -> Json<ApiResponse<Vec<Order>>> {
104
+ let orders = state.orders.lock().await.clone();
105
+ Json(ApiResponse {
106
+ success: true,
107
+ data: Some(orders),
108
+ error: None,
109
+ })
110
+ }
111
+
112
+ async fn create_order(
113
+ State(state): State<AppState>,
114
+ Json(req): Json<CreateOrderRequest>,
115
+ ) -> Json<ApiResponse<Order>> {
116
+ let order = Order {
117
+ id: format!("ORD-{}", chrono::Utc::now().timestamp_millis()),
118
+ symbol: req.symbol.clone(),
119
+ side: req.side.clone(),
120
+ order_type: if req.price.is_some() { "LIMIT".to_string() } else { "MARKET".to_string() },
121
+ quantity: req.quantity,
122
+ price: req.price,
123
+ status: "pending".to_string(),
124
+ created_at: Utc::now(),
125
+ };
126
+
127
+ state.orders.lock().await.push(order.clone());
128
+
129
+ Json(ApiResponse {
130
+ success: true,
131
+ data: Some(order),
132
+ error: None,
133
+ })
134
+ }
135
+
136
+ async fn close_position(
137
+ State(state): State<AppState>,
138
+ Json(req): Json<ClosePositionRequest>,
139
+ ) -> Json<ApiResponse<Position>> {
140
+ let mut positions = state.positions.lock().await;
141
+
142
+ if let Some(pos) = positions.iter_mut().find(|p| p.id == req.position_id) {
143
+ let current_price = pos.current_price;
144
+ let pnl = if pos.side == "BUY" {
145
+ (current_price - pos.entry_price) * pos.quantity
146
+ } else {
147
+ (pos.entry_price - current_price) * pos.quantity
148
+ };
149
+
150
+ pos.pnl = pnl;
151
+
152
+ Json(ApiResponse {
153
+ success: true,
154
+ data: Some(pos.clone()),
155
+ error: None,
156
+ })
157
+ } else {
158
+ Json(ApiResponse {
159
+ success: false,
160
+ data: None,
161
+ error: Some("Position not found".to_string()),
162
+ })
163
+ }
164
+ }
165
+
166
+ async fn analyze_market(
167
+ State(state): State<AppState>,
168
+ Json(req): Json<serde_json::Value>,
169
+ ) -> Json<ApiResponse<serde_json::Value>> {
170
+ let symbol = req.get("symbol")
171
+ .and_then(|s| s.as_str())
172
+ .unwrap_or("BTCUSDT");
173
+
174
+ let analysis = serde_json::json!({
175
+ "symbol": symbol,
176
+ "recommendation": "BUY",
177
+ "confidence": 0.75,
178
+ "reason": "RSI oversold, trend bullish",
179
+ "entry_price": 50000.0,
180
+ "stop_loss": 47500.0,
181
+ "take_profit": 55000.0,
182
+ "risk_level": "MEDIUM"
183
+ });
184
+
185
+ Json(ApiResponse {
186
+ success: true,
187
+ data: Some(analysis),
188
+ error: None,
189
+ })
190
+ }
191
+
192
+ #[tokio::main]
193
+ async fn main() {
194
+ tracing_subscriber::fmt::init();
195
+
196
+ let config = TradingConfig {
197
+ api_key: std::env::var("BINANCE_API_KEY").unwrap_or_default(),
198
+ api_secret: std::env::var("BINANCE_API_SECRET").unwrap_or_default(),
199
+ testnet: true,
200
+ max_position_size: 1000.0,
201
+ stop_loss_percent: 5.0,
202
+ take_profit_percent: 10.0,
203
+ };
204
+
205
+ let state = AppState {
206
+ config,
207
+ positions: Arc::new(Mutex::new(Vec::new())),
208
+ orders: Arc::new(Mutex::new(Vec::new())),
209
+ };
210
+
211
+ let app = Router::new()
212
+ .route("/", get(health))
213
+ .route("/health", get(health))
214
+ .route("/api/balance", get(get_balance))
215
+ .route("/api/positions", get(get_positions))
216
+ .route("/api/orders", get(get_orders))
217
+ .route("/api/orders", post(create_order))
218
+ .route("/api/positions/close", post(close_position))
219
+ .route("/api/analyze", post(analyze_market))
220
+ .with_state(state);
221
+
222
+ let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
223
+
224
+ tracing::info!("RapidLiveClient API running on http://0.0.0.0:3000");
225
+
226
+ axum::serve(listener, app).await.unwrap();
227
+ }
rust-api/src/trading.rs ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Trading module placeholder
2
+ // This would contain Nautilus Trader strategy integration
3
+
4
+ use serde::{Deserialize, Serialize};
5
+
6
+ #[derive(Debug, Clone, Serialize, Deserialize)]
7
+ pub struct StrategyConfig {
8
+ pub name: String,
9
+ pub symbols: Vec<String>,
10
+ pub capital: f64,
11
+ pub risk_per_trade: f64,
12
+ pub max_positions: usize,
13
+ }
14
+
15
+ impl Default for StrategyConfig {
16
+ fn default() -> Self {
17
+ Self {
18
+ name: "RapidAgentStrategy".to_string(),
19
+ symbols: vec![
20
+ "BTCUSDT".to_string(),
21
+ "ETHUSDT".to_string(),
22
+ "SOLUSDT".to_string(),
23
+ ],
24
+ capital: 10000.0,
25
+ risk_per_trade: 0.02,
26
+ max_positions: 3,
27
+ }
28
+ }
29
+ }
30
+
31
+ pub struct TradingStrategy {
32
+ config: StrategyConfig,
33
+ }
34
+
35
+ impl TradingStrategy {
36
+ pub fn new(config: StrategyConfig) -> Self {
37
+ Self { config }
38
+ }
39
+
40
+ pub fn should_entry(&self, symbol: &str, rsi: f64, trend: &str) -> bool {
41
+ if rsi < 30.0 && trend == "bullish" {
42
+ return true;
43
+ }
44
+ if rsi < 40.0 && trend == "bullish" {
45
+ return true;
46
+ }
47
+ false
48
+ }
49
+
50
+ pub fn should_exit(&self, symbol: &str, rsi: f64, pnl_percent: f64) -> bool {
51
+ if rsi > 70.0 {
52
+ return true;
53
+ }
54
+ if pnl_percent >= self.config.risk_per_trade * 5.0 {
55
+ return true;
56
+ }
57
+ if pnl_percent <= -self.config.risk_per_trade * 2.0 {
58
+ return true;
59
+ }
60
+ false
61
+ }
62
+
63
+ pub fn calculate_position_size(&self, capital: f64, entry: f64, stop_loss: f64) -> f64 {
64
+ let risk_amount = capital * self.config.risk_per_trade;
65
+ let risk_per_unit = (entry - stop_loss).abs() / entry;
66
+ risk_amount / risk_per_unit
67
+ }
68
+ }