//+------------------------------------------------------------------+ //| QuarterTheory_OPTIMIZED.mq5 | //| OPTIMIZED: Wide SL + Strong MA TPs + Liquidity Sweeps | //| Stronger MAs (230+) are Major Triggers & TPs - Stay in Longer | //+------------------------------------------------------------------+ #property copyright "Optimized System - Hit TPs, Re-enter Sweeps" #property version "9.00" #property strict #include CTrade Trade; //================ INPUT PARAMETERS ==================// input group "=== GENERAL ===" input int MagicNumber = 456789; input double Risk_Per_Trade = 1.5; input int Max_Simultaneous_Trades = 6; input group "=== RISK MANAGEMENT (WIDER SL) ===" input int Initial_SL_Points = 300; // WIDER! 30 pips for Gold input int BreakEven_Points = 100; // Later BE input bool Use_Wider_SL_On_Strong = true; // Even wider on 3+ MA alignment input int Strong_Signal_SL_Points = 450; // 45 pips when 3+ MAs input group "=== MOVING AVERAGES ===" input int MA_1 = 7; // Fast - Entry signal input int MA_2 = 14; // Fast - Entry signal input int MA_3 = 21; // Fast - Entry signal input int MA_4 = 50; // Medium - Entry signal input int MA_5 = 140; // Medium - Entry signal input int MA_6 = 230; // STRONG - Major trigger/TP input int MA_7 = 500; // STRONG - Major TP input int MA_8 = 1000; // STRONG - Major TP input int MA_9 = 1100; // STRONG - Major TP input int MA_10 = 1300; // STRONG - Final TP input group "=== MA STRENGTH RULES ===" input bool Strong_MAs_Are_Magnets = true; // 230+ are powerful TPs input int MA_Touch_Buffer_Points = 20; // Buffer for MA "kiss" input int Min_Aligned_For_Entry = 3; // Min MAs aligned input int Strong_Aligned_Count = 5; // 5+ = very strong input bool Stay_In_On_Strong = true; // Hold longer if 5+ aligned input group "=== LIQUIDITY SWEEP RE-ENTRY ===" input bool Trade_Liquidity_Sweeps = true; // Re-enter after SL hit input int Sweep_Detection_Points = 50; // How far past level = sweep input int Sweep_Cooldown_Bars = 3; // Wait 3 bars after sweep input bool Only_Sweep_At_Fib = true; // Only sweep at Fib levels input group "=== DYNAMIC TAKE PROFIT ===" // WEAK (3 MAs aligned) input double Weak_Close_At_MA6 = 20.0; // Close 20% at MA 230 input double Weak_Close_At_MA7 = 30.0; // Close 30% at MA 500 input double Weak_Close_At_MA8 = 30.0; // Close 30% at MA 1000 // MEDIUM (4 MAs aligned) input double Medium_Close_At_MA6 = 15.0; // Close 15% at MA 230 input double Medium_Close_At_MA7 = 25.0; // Close 25% at MA 500 input double Medium_Close_At_MA8 = 25.0; // Close 25% at MA 1000 // STRONG (5+ MAs aligned) input double Strong_Close_At_MA6 = 10.0; // Close 10% at MA 230 input double Strong_Close_At_MA7 = 20.0; // Close 20% at MA 500 input double Strong_Close_At_MA8 = 25.0; // Close 25% at MA 1000 // Remaining runs to MA 1100 and 1300 input group "=== TRAILING STRATEGY ===" input bool Trail_To_Strong_MAs = true; // Trail SL to 230+ input int Trail_Start_Points = 150; // Start trailing input int Trail_Step_Points = 30; input int Trail_Stop_Points = 50; input group "=== PRICE LEVELS ===" input int Lookback_Bars = 200; input int Level_Buffer_Points = 40; input bool Show_Levels = true; input group "=== DAILY LIMITS ===" input double Max_Daily_Loss_Percent = 4.0; // Higher tolerance input double Max_Daily_Profit_Percent = 20.0; // Higher target input int Max_Trades_Per_Day = 25; //================ GLOBALS ==================// double PriceLevels[]; string LevelTypes[]; int TotalLevels = 0; int MA_Handles[10]; double MA_Current[10]; double MA_Previous[10]; int TRIX_Handle; double TRIX_Current = 0; struct Position { ulong ticket; double entry; double original_lot; bool is_buy; int ma_strength; // 3=weak, 4=medium, 5+=strong double last_sl_hit; // Track liquidity sweeps datetime sweep_time; bool tp_ma6_hit; // MA 230 bool tp_ma7_hit; // MA 500 bool tp_ma8_hit; // MA 1000 bool tp_ma9_hit; // MA 1100 }; Position OpenPositions[]; struct SweepMemory { double level; bool was_buy; datetime time; int bars_ago; }; SweepMemory RecentSweeps[50]; int TotalSweeps = 0; double DailyStart = 0; int TodayTrades = 0; datetime LastDay = 0; //+------------------------------------------------------------------+ int OnInit() { Print("========================================"); Print("OPTIMIZED EA v9.0"); Print("Wide SL + Strong MA TPs + Liquidity Sweeps"); Print("========================================"); Trade.SetExpertMagicNumber(MagicNumber); Trade.SetDeviationInPoints(50); // Initialize MAs int periods[10] = {MA_1, MA_2, MA_3, MA_4, MA_5, MA_6, MA_7, MA_8, MA_9, MA_10}; for(int i=0; i<10; i++) { MA_Handles[i] = iMA(_Symbol, PERIOD_CURRENT, periods[i], 0, MODE_EMA, PRICE_CLOSE); if(MA_Handles[i] == INVALID_HANDLE) { Print("ERROR: MA ", periods[i], " failed"); return INIT_FAILED; } } TRIX_Handle = iMA(_Symbol, PERIOD_CURRENT, 18, 0, MODE_EMA, PRICE_CLOSE); CalculatePriceLevels(); if(Show_Levels) DrawLevels(); DailyStart = AccountInfoDouble(ACCOUNT_BALANCE); Print("Entry: ", Min_Aligned_For_Entry, "+ MAs | Strong: ", Strong_Aligned_Count, "+"); Print("SL: ", Initial_SL_Points, " pts | Strong: ", Strong_Signal_SL_Points, " pts"); Print("Strong MAs (230+) are MAJOR TPs"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { for(int i=0; i<10; i++) IndicatorRelease(MA_Handles[i]); IndicatorRelease(TRIX_Handle); ObjectsDeleteAll(0, "Level_"); ObjectsDeleteAll(0, "Arrow_"); ObjectsDeleteAll(0, "Sweep_"); Print("EA Stopped"); } //+------------------------------------------------------------------+ void CalculatePriceLevels() { ArrayResize(PriceLevels, 0); ArrayResize(LevelTypes, 0); TotalLevels = 0; double high = iHigh(_Symbol, PERIOD_CURRENT, 1); double low = iLow(_Symbol, PERIOD_CURRENT, 1); for(int i=2; i<=Lookback_Bars; i++) { double h = iHigh(_Symbol, PERIOD_CURRENT, i); double l = iLow(_Symbol, PERIOD_CURRENT, i); if(h > high) high = h; if(l < low) low = l; } double range = high - low; // Fibonacci levels AddLevel(low, "FIB_0.0"); AddLevel(low + range * 0.236, "FIB_0.236"); AddLevel(low + range * 0.382, "FIB_0.382"); AddLevel(low + range * 0.5, "FIB_0.5"); AddLevel(low + range * 0.618, "FIB_0.618"); AddLevel(low + range * 0.786, "FIB_0.786"); AddLevel(high, "FIB_1.0"); AddLevel(high + range * 0.618, "FIB_1.618"); // Quarter + Eighth AddLevel(low + range * 0.25, "QUARTER_0.25"); AddLevel(low + range * 0.75, "QUARTER_0.75"); AddLevel(low + range * 0.125, "EIGHTH_0.125"); AddLevel(low + range * 0.375, "EIGHTH_0.375"); AddLevel(low + range * 0.625, "EIGHTH_0.625"); AddLevel(low + range * 0.875, "EIGHTH_0.875"); Print("Levels calculated: ", TotalLevels); } void AddLevel(double price, string type) { int size = ArraySize(PriceLevels); ArrayResize(PriceLevels, size+1); ArrayResize(LevelTypes, size+1); PriceLevels[size] = price; LevelTypes[size] = type; TotalLevels++; } void DrawLevels() { ObjectsDeleteAll(0, "Level_"); for(int i=0; i= 0 || StringFind(LevelTypes[i], "FIB_0.618") >= 0) { clr = clrYellow; width = 2; } else if(StringFind(LevelTypes[i], "FIB") >= 0) { clr = clrDodgerBlue; } ObjectCreate(0, name, OBJ_HLINE, 0, 0, PriceLevels[i]); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(0, name, OBJPROP_WIDTH, width); ObjectSetInteger(0, name, OBJPROP_BACK, true); ObjectSetString(0, name, OBJPROP_TEXT, LevelTypes[i]); } } //+------------------------------------------------------------------+ void UpdateMAs() { for(int i=0; i<10; i++) { double curr[1], prev[1]; CopyBuffer(MA_Handles[i], 0, 0, 1, curr); CopyBuffer(MA_Handles[i], 0, 1, 1, prev); MA_Current[i] = curr[0]; MA_Previous[i] = prev[0]; } } //+------------------------------------------------------------------+ //| COUNT ALIGNED MAs (Strength detector) //+------------------------------------------------------------------+ int CountAlignedMAs(bool bullish) { UpdateMAs(); int aligned = 0; // Check all MAs for proper stacking for(int i=0; i<9; i++) { if(bullish) { if(MA_Current[i] > MA_Current[i+1]) aligned++; } else { if(MA_Current[i] < MA_Current[i+1]) aligned++; } } return aligned; } //+------------------------------------------------------------------+ //| CHECK IF PRICE TOUCHING STRONG MA (230+) //+------------------------------------------------------------------+ bool IsPriceAtStrongMA(double price, bool &at_ma230, bool &at_ma500, bool &at_ma1000) { double buffer = MA_Touch_Buffer_Points * _Point; at_ma230 = MathAbs(price - MA_Current[5]) <= buffer; at_ma500 = MathAbs(price - MA_Current[6]) <= buffer; at_ma1000 = MathAbs(price - MA_Current[7]) <= buffer; return (at_ma230 || at_ma500 || at_ma1000); } //+------------------------------------------------------------------+ //| CHECK FOR LIQUIDITY SWEEP //+------------------------------------------------------------------+ bool IsLiquiditySweep(double current_price, bool check_buy, bool &is_fib) { if(!Trade_Liquidity_Sweeps) return false; is_fib = false; double sweep_dist = Sweep_Detection_Points * _Point; // Look for recent price that swept past a level then reversed for(int i=0; i= 0; if(Only_Sweep_At_Fib && !level_is_fib) continue; if(check_buy) { // Bullish sweep: Price dropped below level, now back above double low_recent = iLow(_Symbol, PERIOD_CURRENT, 1); double low_2 = iLow(_Symbol, PERIOD_CURRENT, 2); if((low_recent < level || low_2 < level) && current_price > level + sweep_dist) { is_fib = level_is_fib; Print("LIQUIDITY SWEEP detected: Bulls | Level: ", level, " | Type: ", LevelTypes[i]); return true; } } else { // Bearish sweep: Price spiked above level, now back below double high_recent = iHigh(_Symbol, PERIOD_CURRENT, 1); double high_2 = iHigh(_Symbol, PERIOD_CURRENT, 2); if((high_recent > level || high_2 > level) && current_price < level - sweep_dist) { is_fib = level_is_fib; Print("LIQUIDITY SWEEP detected: Bears | Level: ", level, " | Type: ", LevelTypes[i]); return true; } } } return false; } //+------------------------------------------------------------------+ bool IsPriceAtLevel(double price) { double buffer = Level_Buffer_Points * _Point; for(int i=0; i= Strong_Aligned_Count) risk_multiplier = 1.2; // Take 20% more risk on strong setups double risk = AccountInfoDouble(ACCOUNT_BALANCE) * Risk_Per_Trade * risk_multiplier / 100.0; double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double lot = risk / ((sl_points * _Point / tickSize) * tickValue); double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); lot = MathMax(lot, minLot); lot = MathMin(lot, maxLot); lot = NormalizeDouble(lot / step, 0) * step; return lot; } //+------------------------------------------------------------------+ bool CheckLimits() { MqlDateTime dt; TimeCurrent(dt); dt.hour = 0; dt.min = 0; dt.sec = 0; datetime today = StructToTime(dt); if(today != LastDay) { DailyStart = AccountInfoDouble(ACCOUNT_BALANCE); TodayTrades = 0; LastDay = today; } if(TodayTrades >= Max_Trades_Per_Day) return false; double balance = AccountInfoDouble(ACCOUNT_BALANCE); double pl = ((balance - DailyStart) / DailyStart) * 100.0; if(pl <= -Max_Daily_Loss_Percent || pl >= Max_Daily_Profit_Percent) return false; return true; } //+------------------------------------------------------------------+ void OpenTrade(bool buy, double price, int ma_strength, string reason) { // Determine SL size based on strength int sl_points = Initial_SL_Points; if(Use_Wider_SL_On_Strong && ma_strength >= Strong_Aligned_Count) sl_points = Strong_Signal_SL_Points; double lot = GetLotSize(sl_points, ma_strength); if(lot < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN)) return; double sl = buy ? price - sl_points * _Point : price + sl_points * _Point; string strength_text = "WEAK"; if(ma_strength >= Strong_Aligned_Count) strength_text = "STRONG"; else if(ma_strength >= 4) strength_text = "MEDIUM"; string comment = strength_text + " | " + IntegerToString(ma_strength) + "MAs | " + reason; bool result = false; if(buy) result = Trade.Buy(lot, _Symbol, price, sl, 0, comment); else result = Trade.Sell(lot, _Symbol, price, sl, 0, comment); if(result) { TodayTrades++; ulong ticket = Trade.ResultOrder(); int size = ArraySize(OpenPositions); ArrayResize(OpenPositions, size+1); OpenPositions[size].ticket = ticket; OpenPositions[size].entry = price; OpenPositions[size].original_lot = lot; OpenPositions[size].is_buy = buy; OpenPositions[size].ma_strength = ma_strength; OpenPositions[size].tp_ma6_hit = false; OpenPositions[size].tp_ma7_hit = false; OpenPositions[size].tp_ma8_hit = false; OpenPositions[size].tp_ma9_hit = false; Print("========== TRADE ", TodayTrades, " =========="); Print(buy ? "BUY" : "SELL", " @ ", price); Print("Strength: ", strength_text, " (", ma_strength, " MAs)"); Print("SL: ", sl_points, " points"); Print("Reason: ", reason); Print("==================================="); // Draw arrow string arrow_name = "Arrow_" + IntegerToString(ticket); ObjectCreate(0, arrow_name, OBJ_ARROW, 0, TimeCurrent(), price); ObjectSetInteger(0, arrow_name, OBJPROP_COLOR, buy ? clrLime : clrRed); ObjectSetInteger(0, arrow_name, OBJPROP_ARROWCODE, buy ? 233 : 234); ObjectSetInteger(0, arrow_name, OBJPROP_WIDTH, 3); } } //+------------------------------------------------------------------+ void ManagePositions() { UpdateMAs(); for(int i=PositionsTotal()-1; i>=0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; int idx = -1; for(int j=0; j= Strong_Aligned_Count) be_threshold = BreakEven_Points + 50; // Give it more room if(profit_points >= be_threshold) { if((is_buy && sl < entry) || (!is_buy && sl > entry)) { Trade.PositionModify(ticket, entry, 0); Print("BE: Position ", ticket, " | Strength: ", strength); } } double lot = PositionGetDouble(POSITION_VOLUME); // DYNAMIC TP based on strength double close_ma6_pct, close_ma7_pct, close_ma8_pct; if(strength >= Strong_Aligned_Count) { close_ma6_pct = Strong_Close_At_MA6; close_ma7_pct = Strong_Close_At_MA7; close_ma8_pct = Strong_Close_At_MA8; } else if(strength >= 4) { close_ma6_pct = Medium_Close_At_MA6; close_ma7_pct = Medium_Close_At_MA7; close_ma8_pct = Medium_Close_At_MA8; } else { close_ma6_pct = Weak_Close_At_MA6; close_ma7_pct = Weak_Close_At_MA7; close_ma8_pct = Weak_Close_At_MA8; } // TP at MA 230 (Strong MA!) if(!OpenPositions[idx].tp_ma6_hit) { bool hit = is_buy ? (current >= MA_Current[5]) : (current <= MA_Current[5]); if(hit) { double close_size = NormalizeDouble(OpenPositions[idx].original_lot * close_ma6_pct / 100.0, 2); if(close_size > 0 && close_size <= lot) { Trade.PositionClosePartial(ticket, close_size); OpenPositions[idx].tp_ma6_hit = true; Print("TP: MA 230 hit | Closed ", close_ma6_pct, "% | Ticket: ", ticket); } } } // TP at MA 500 if(!OpenPositions[idx].tp_ma7_hit) { bool hit = is_buy ? (current >= MA_Current[6]) : (current <= MA_Current[6]); if(hit) { double close_size = NormalizeDouble(OpenPositions[idx].original_lot * close_ma7_pct / 100.0, 2); if(close_size > 0 && close_size <= lot) { Trade.PositionClosePartial(ticket, close_size); OpenPositions[idx].tp_ma7_hit = true; Print("TP: MA 500 hit | Closed ", close_ma7_pct, "% | Ticket: ", ticket); } } } // TP at MA 1000 if(!OpenPositions[idx].tp_ma8_hit) { bool hit = is_buy ? (current >= MA_Current[7]) : (current <= MA_Current[7]); if(hit) { double close_size = NormalizeDouble(OpenPositions[idx].original_lot * close_ma8_pct / 100.0, 2); if(close_size > 0 && close_size <= lot) { Trade.PositionClosePartial(ticket, close_size); OpenPositions[idx].tp_ma8_hit = true; Print("TP: MA 1000 hit | Closed ", close_ma8_pct, "% | Ticket: ", ticket); } } } // Trailing - to strong MAs or fixed points if(profit_points >= Trail_Start_Points) { if(Trail_To_Strong_MAs) { // Trail to highest broken strong MA double trail_level = 0; if(is_buy) { // Trail to highest MA below current price if(MA_Current[5] < current && MA_Current[5] > trail_level) // 230 trail_level = MA_Current[5]; if(MA_Current[6] < current && MA_Current[6] > trail_level) // 500 trail_level = MA_Current[6]; if(MA_Current[7] < current && MA_Current[7] > trail_level) // 1000 trail_level = MA_Current[7]; if(MA_Current[8] < current && MA_Current[8] > trail_level) // 1100 trail_level = MA_Current[8]; if(trail_level > sl + Trail_Step_Points * _Point) { Trade.PositionModify(ticket, trail_level, 0); Print("Trail: SL → MA level ", trail_level); } } else { // Trail to lowest MA above current price trail_level = 999999; if(MA_Current[5] > current && MA_Current[5] < trail_level) trail_level = MA_Current[5]; if(MA_Current[6] > current && MA_Current[6] < trail_level) trail_level = MA_Current[6]; if(MA_Current[7] > current && MA_Current[7] < trail_level) trail_level = MA_Current[7]; if(MA_Current[8] > current && MA_Current[8] < trail_level) trail_level = MA_Current[8]; if(trail_level < 999999 && trail_level < sl - Trail_Step_Points * _Point) { Trade.PositionModify(ticket, trail_level, 0); Print("Trail: SL → MA level ", trail_level); } } } else { // Standard trailing double newSL = is_buy ? current - Trail_Stop_Points * _Point : current + Trail_Stop_Points * _Point; if((is_buy && newSL > sl + Trail_Step_Points * _Point) || (!is_buy && newSL < sl - Trail_Step_Points * _Point)) { Trade.PositionModify(ticket, newSL, 0); } } } } } //+------------------------------------------------------------------+ void OnTick() { ManagePositions(); if(!CheckLimits()) return; int open = 0; for(int i=0; i= Max_Simultaneous_Trades) return; static datetime last_calc = 0; if(TimeCurrent() - last_calc > 1800) { CalculatePriceLevels(); if(Show_Levels) DrawLevels(); last_calc = TimeCurrent(); } double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double current = (bid + ask) / 2; // Count aligned MAs (strength) int bull_strength = CountAlignedMAs(true); int bear_strength = CountAlignedMAs(false); // Check if at price level bool at_level = IsPriceAtLevel(current); // Check if touching strong MAs bool at_ma230=false, at_ma500=false, at_ma1000=false; bool at_strong_ma = IsPriceAtStrongMA(current, at_ma230, at_ma500, at_ma1000); // Check for liquidity sweeps bool is_fib_sweep = false; bool bull_sweep = IsLiquiditySweep(current, true, is_fib_sweep); bool bear_sweep = IsLiquiditySweep(current, false, is_fib_sweep); // === ENTRY LOGIC === // BUY signals bool buy_signal = false; string buy_reason = ""; // 1. Strong MAs aligned + at level if(bull_strength >= Min_Aligned_For_Entry && at_level) { buy_signal = true; buy_reason = IntegerToString(bull_strength) + " MAs + Level"; } // 2. Touching strong MA (230+) + MAs aligned if(Strong_MAs_Are_Magnets && bull_strength >= Min_Aligned_For_Entry && at_strong_ma) { buy_signal = true; if(at_ma230) buy_reason = "MA 230 Kiss + " + IntegerToString(bull_strength) + " MAs"; else if(at_ma500) buy_reason = "MA 500 Kiss + " + IntegerToString(bull_strength) + " MAs"; else if(at_ma1000) buy_reason = "MA 1000 Kiss + " + IntegerToString(bull_strength) + " MAs"; } // 3. Liquidity sweep + strong alignment if(bull_sweep && bull_strength >= Min_Aligned_For_Entry) { buy_signal = true; buy_reason = "Liquidity Sweep (Bull) + " + IntegerToString(bull_strength) + " MAs"; } // 4. Very strong alignment (5+) = auto entry at any level if(bull_strength >= Strong_Aligned_Count && at_level) { buy_signal = true; buy_reason = "STRONG " + IntegerToString(bull_strength) + " MAs ALIGNED!"; } // SELL signals bool sell_signal = false; string sell_reason = ""; // 1. Strong MAs aligned + at level if(bear_strength >= Min_Aligned_For_Entry && at_level) { sell_signal = true; sell_reason = IntegerToString(bear_strength) + " MAs + Level"; } // 2. Touching strong MA (230+) + MAs aligned if(Strong_MAs_Are_Magnets && bear_strength >= Min_Aligned_For_Entry && at_strong_ma) { sell_signal = true; if(at_ma230) sell_reason = "MA 230 Kiss + " + IntegerToString(bear_strength) + " MAs"; else if(at_ma500) sell_reason = "MA 500 Kiss + " + IntegerToString(bear_strength) + " MAs"; else if(at_ma1000) sell_reason = "MA 1000 Kiss + " + IntegerToString(bear_strength) + " MAs"; } // 3. Liquidity sweep + strong alignment if(bear_sweep && bear_strength >= Min_Aligned_For_Entry) { sell_signal = true; sell_reason = "Liquidity Sweep (Bear) + " + IntegerToString(bear_strength) + " MAs"; } // 4. Very strong alignment (5+) = auto entry if(bear_strength >= Strong_Aligned_Count && at_level) { sell_signal = true; sell_reason = "STRONG " + IntegerToString(bear_strength) + " MAs ALIGNED!"; } // EXECUTE if(buy_signal) { OpenTrade(true, ask, bull_strength, buy_reason); } if(sell_signal) { OpenTrade(false, bid, bear_strength, sell_reason); } } //+------------------------------------------------------------------+