import type { UserSettings } from "../models/UserSettings";
import type { StrategyKey, StrategyOption } from "../types/Strategy";
import { fetchStrategies } from "../services/strategyService";
-
import { selectStrategy } from "../services/api/strategyApi";
+type Pair = "BTC/EUR" | "BTC/USDT";
+
+function getPair(settings: UserSettings): Pair {
+ return settings.currency === "USDT" ? "BTC/USDT" : "BTC/EUR";
+}
+
export default function StrategyScreen() {
const [settings, setSettings] = useState<UserSettings | null>(null);
const [strategies, setStrategies] = useState<StrategyOption[]>([]);
setBusy(true);
setInfo(null);
- // Pair alignée DB/serveur : EUR -> BTC/EUR ; USDT -> BTC/USDT
- const quote = settings.currency === "USDT" ? "USDT" : "EUR";
- const pair = `BTC/${quote}`;
+ const pair = getPair(settings); // ✅ type "BTC/EUR" | "BTC/USDT"
- // 1) Serveur d’abord (source de vérité)
+ // 1) Serveur d’abord
await selectStrategy({ pair, mode: key, params: {} });
- // 2) Puis local (affichage immédiat)
+ // 2) Local ensuite (affichage immédiat)
const next: UserSettings = { ...settings, selectedStrategyKey: key };
await saveSettings(next);
setSettings(next);
- setInfo(`Stratégie sélectionnée : ${key} ✅`);
+ setInfo("Stratégie sélectionnée ✅");
} catch (e: any) {
// rollback local si besoin
const rollback: UserSettings = { ...settings, selectedStrategyKey: prev };
await saveSettings(rollback);
setSettings(rollback);
- setInfo(`Impossible de sélectionner la stratégie. (${e?.message ?? "Erreur inconnue"})`);
+ setInfo(`Impossible de sélectionner la stratégie (${e?.message ?? "erreur"}).`);
} finally {
setBusy(false);
}
<View style={ui.card}>
<Text style={ui.title}>Stratégie</Text>
<Text style={ui.muted}>
- Choisissez une stratégie. Elle est enregistrée sur le serveur.
+ Choisissez un mode d’analyse. Il est enregistré sur le serveur.
</Text>
<Text style={[ui.muted, { marginTop: 8 }]}>
Actuelle : <Text style={styles.boldInline}>{settings.selectedStrategyKey}</Text>
</Text>
- {!!info && <Text style={[ui.muted, { marginTop: 8 }]}>{info}</Text>}
+ <Text style={[ui.muted, { marginTop: 6 }]}>
+ Paire : <Text style={styles.boldInline}>{getPair(settings)}</Text>
+ </Text>
+
+ {!!info && <Text style={[ui.muted, { marginTop: 10 }]}>{info}</Text>}
</View>
}
renderItem={({ item }) => (
isSelected(item.key) && styles.btnSelected,
busy && styles.btnDisabled,
]}
- onPress={() => void handleSelect(item.key)}
+ onPress={() => handleSelect(item.key)}
disabled={busy}
>
<Text style={ui.buttonText}>
import type { StrategyKey } from "../../types/Strategy";
/**
- * strategyApi — aligné strategy-service (Stéphane)
+ * strategyApi — aligné strategy-service (Sacha)
* POST /api/strategy/select
* body: { userId, pairId, mode, params }
- *
- * IMPORTANT:
- * - pairId est obligatoire côté serveur.
- * - params est mieux en JSON string (stocké DB).
*/
function pairToPairId(pair: string): number {
const p = pair.trim().toUpperCase();
-
if (p === "BTC/EUR") return 1;
if (p === "BTC/USDT") return 2;
-
- throw new Error(`Pair non supportée par la DB (pairs) : ${pair}`);
+ throw new Error(`Pair non supportée par la DB : ${pair}`);
}
export async function selectStrategy(params: {
- pair: string; // ex: "BTC/EUR"
- mode: StrategyKey; // ex: "RSI_SIMPLE"
+ pair: "BTC/EUR" | "BTC/USDT";
+ mode: StrategyKey; // SAFE | STANDARD | GREEDY
params?: Record<string, unknown>;
}): Promise<void> {
const session = await loadSession();
const userId = session?.userId;
- if (!userId) throw new Error("Session absente : impossible de sélectionner une stratégie.");
+ if (!userId) throw new Error("Session absente.");
- const pair = params.pair.trim().toUpperCase();
- const pairId = pairToPairId(pair);
+ const pairId = pairToPairId(params.pair);
await apiPost(`/strategy/select`, {
userId,
- pairId, // ✅ requis serveur
- mode: params.mode,
- params: JSON.stringify(params.params ?? {}), // ✅ stockable DB
+ pairId,
+ mode: params.mode, // ✅ exactement ce que StrategyFactory attend
+ params: JSON.stringify(params.params ?? {}),
});
}
\ No newline at end of file
-import type { StrategyOption, StrategyKey } from "../types/Strategy";
-import { apiGet } from "./api/http";
-
-type Risk = "SAFE" | "NORMAL" | "AGGRESSIVE";
-
-function asArray(x: any): any[] {
- if (Array.isArray(x)) return x;
- if (Array.isArray(x?.strategies)) return x.strategies;
- if (Array.isArray(x?.items)) return x.items;
- if (Array.isArray(x?.data)) return x.data;
- return [];
-}
-
-function normalizeRisk(raw: any): Risk {
- const v = String(raw ?? "").toUpperCase();
- if (v === "SAFE") return "SAFE";
- if (v === "AGGRESSIVE") return "AGGRESSIVE";
- if (v === "NORMAL") return "NORMAL";
- return "NORMAL";
-}
-
-function normalizeStrategy(raw: any): StrategyOption | null {
- const key = String(raw?.key ?? raw?.mode ?? raw?.strategy_key ?? raw?.strategyKey ?? "").trim();
- if (!key) return null;
-
- return {
- key: key as StrategyKey,
- label: String(raw?.label ?? raw?.name ?? key),
- description: String(raw?.description ?? "—"),
- risk: normalizeRisk(raw?.risk ?? raw?.level ?? raw?.modeRisk),
- };
-}
-
+import type { StrategyOption } from "../types/Strategy";
+
+/**
+ * strategyService
+ * ---------------
+ * Le serveur ne fournit pas encore /api/strategy/list,
+ * donc on expose une liste locale alignée sur les modes serveur.
+ */
export async function fetchStrategies(): Promise<StrategyOption[]> {
- const candidates = ["/strategy/list", "/strategy/available", "/strategy/modes"];
-
- for (const path of candidates) {
- try {
- const data = await apiGet<any>(path);
- const arr = asArray(data);
- const normalized = arr.map(normalizeStrategy).filter(Boolean) as StrategyOption[];
- if (normalized.length > 0) return normalized;
- } catch {
- // continue
- }
- }
-
- // Fallback “propre” (sans mocks)
return [
{
- key: "RSI_SIMPLE" as StrategyKey,
- label: "RSI Simple",
- description: "Signal basé sur RSI (surachat / survente).",
- risk: "NORMAL",
+ key: "SAFE",
+ label: "Sécurisée",
+ description: "Approche prudente, évite les signaux trop agressifs.",
+ risk: "SAFE",
},
{
- key: "EMA_CROSS" as StrategyKey,
- label: "EMA Cross",
- description: "Croisement de moyennes mobiles exponentielles.",
+ key: "STANDARD",
+ label: "Standard",
+ description: "Équilibre entre prudence et réactivité (mode par défaut).",
risk: "NORMAL",
},
{
- key: "ALWAYS_HOLD" as StrategyKey,
- label: "Always HOLD",
- description: "Ne déclenche jamais d’achat/vente.",
- risk: "SAFE",
+ key: "GREEDY",
+ label: "Agressive",
+ description: "Plus réactive, recherche davantage d’opportunités (plus risqué).",
+ risk: "AGGRESSIVE",
},
];
-}
-
-export async function selectStrategy(_strategyKey: StrategyKey): Promise<void> {
- return;
}
\ No newline at end of file
/**
* Strategy
* --------
- * Contrat simple côté mobile.
- * Plus tard: ces données viendront de l'API / DB.
+ * Aligné avec strategy-service (Sacha).
+ *
+ * Modes supportés par le serveur :
+ * - SAFE
+ * - STANDARD
+ * - GREEDY
*/
-
-export type StrategyKey =
- | "RSI_SIMPLE"
- | "MA_CROSS"
- | "MACD_BASIC"
- | "HOLD_ONLY";
+export type StrategyKey = "SAFE" | "STANDARD" | "GREEDY";
export interface StrategyOption {
- key: StrategyKey;
- label: string;
+ key: StrategyKey; // = mode envoyé au serveur
+ label: string; // texte UI libre
description: string;
risk: "SAFE" | "NORMAL" | "AGGRESSIVE";
}
\ No newline at end of file