using System; namespace QuantEngine.Core.Domain { public class ProfitLockResult { public string RatchetStage { get; set; } = "NORMAL"; public double? AutoTrailingStop { get; set; } public string TpLadderAction { get; set; } = "적용 안함"; public bool ApexSuperActive { get; set; } } public static class ProfitLockCalculator { public static string ClassifyProfitLockStage(double profitPct) { if (profitPct >= 60) return "APEX_SUPER"; if (profitPct >= 40) return "APEX_TRAILING"; if (profitPct >= 30) return "PROFIT_LOCK_30"; if (profitPct >= 20) return "PROFIT_LOCK_20"; if (profitPct >= 10) return "PROFIT_LOCK_10"; if (profitPct >= 0) return "BREAKEVEN_RATCHET"; return "NORMAL"; } public static ProfitLockResult ComputeTrailingStop( double profitPct, double highestClose, double atr20, double? ratchetStop, double averageCost) { string stage = ClassifyProfitLockStage(profitPct); double baseStop = ratchetStop ?? averageCost; double? trailingStop = null; string tpAction = "적용 안함"; if (stage == "APEX_SUPER") { double raw = highestClose - 1.2 * atr20; trailingStop = KrxTickNormalizer.NormalizeTick(Math.Max(baseStop, raw)); tpAction = "강제 10% 익절 권고"; } else if (stage == "APEX_TRAILING") { double raw = highestClose - 1.5 * atr20; trailingStop = KrxTickNormalizer.NormalizeTick(Math.Max(baseStop, raw)); tpAction = "부분익절 검토"; } else if (stage == "PROFIT_LOCK_30" || stage == "PROFIT_LOCK_20") { double raw = highestClose - 2.0 * atr20; trailingStop = KrxTickNormalizer.NormalizeTick(Math.Max(baseStop, raw)); tpAction = "래칫 유지"; } return new ProfitLockResult { RatchetStage = stage, AutoTrailingStop = trailingStop, TpLadderAction = tpAction, ApexSuperActive = stage == "APEX_SUPER" }; } } }