Your sweep detector works. It fires on a clean run beyond a prior extreme and a close back inside. Your session filter works — it knows exactly when London is open. Your structure-shift routine works too; you have watched it print the right label on the right bar for weeks.

Then you join all three with &&, run the result over a year of data, and the equity curve looks nothing like the days you trade by hand. The expert advisor buys a sweep at 16:40 that had nothing to do with the morning's range. It takes three trades on a Tuesday and none on the Thursday you would have taken by hand. Twice in the same hour it enters the same move.

Nothing in those modules is broken. What is missing is the one thing none of them can supply on its own: order. Accumulation, manipulation, distribution is not a condition your EA can test for on a single bar — it is a claim about sequence. One thing happens, then another, then a third, once, inside a single trading day. A stateless if has no way to assert that.

So this article is not another detector. It is the layer above them: a per-day phase state machine that knows which part of the cycle price is in right now, advances only when the module that owns the next transition confirms, places one trade when the sequence completes, and stands down cleanly when the day never delivers.

Key Takeaways
  • AMD is a claim about sequence, not a pattern to match: model it as a per-day phase variable with one-way transitions and a hard reset at the broker's daily bar.
  • Every phase needs two exits - forward on confirmation, and sideways to stand-down when its window closes empty. Missing deadlines is what leaves an EA armed into the wrong session.
  • The orchestrator supplies the level and the deadline; the sweep, session and structure modules only answer questions about what it hands them.
  • Validate the accumulation range before trusting it (ATR size band, drift test, edge quality) - a false range makes every later transition meaningless.
Table of Contents (25 min read)Contents

What the AMD Cycle Looks Like When You Have to Code It

When you read the cycle on a chart you are cheating, in a way that is easy to miss. You see the whole day at once. The Asian range, the London raid beneath it, the turn — all of it sits in front of you simultaneously, and your eye assigns the labels backwards from the right edge. Of course the low at 09:15 was "the manipulation": you can see what happened after it.

Your EA gets none of that. It sees one closed bar, then the next, with no memory beyond the variables you deliberately keep alive. It cannot look right. It has to commit to a label before the confirmation exists.

That forces a reframe, and the reframe is the whole article. Stop asking your code "is this an AMD day?" — that question needs the future. Ask it instead:

  • Which phase am I in right now?
  • Has the exit condition for that phase been met yet?
  • Is it too late in the day for that condition to still count?

Three properties follow directly, and every bug in the rest of the build traces back to one of them.

The phase has to persist between ticks. It is a variable your EA carries forward, not something recomputed from scratch on each bar. Recompute it and you are back to pattern matching.

Transitions run one way inside a day. Once the range is frozen you never re-open accumulation. Once an edge is swept the day has a direction. Going backwards is how an EA re-enters the same move three times.

The whole thing resets hard at the day boundary. Yesterday's phase, yesterday's range, yesterday's direction — all of it dies at the new trading day. Skip the reset and the EA drags a stale state forward and never legitimately re-enters accumulation again.

There is a fourth property that shapes the code more than the other three: each transition is owned by a different module, and each one has a deadline. Accumulation ends when a frozen edge is swept — the liquidity module's call. Manipulation ends when structure shifts — a different module's call. Distribution ends when the trade closes or the session does. Your state machine never re-derives any of those answers. It decides when to ask, what level to ask about, and what to do when nobody answers in time.

A daily timeline with Asian, London and New York session boxes below a five-segment phase track, arrows marking where each AMD phase transition fires and where a missed deadline drops the EA into stand-down.
Every AMD transition is bounded by a clock window and a deadline, which is why the state machine needs two exits from every phase.

Designing the Phase State Machine

Five states are enough. Fewer and you cannot express "the day is over, stop looking"; more and you are encoding sub-conditions that belong inside a transition.

stateDiagram-v2
    [*] --> Idle
    Idle --> Accumulation: range valid at window close
    Idle --> StandDown: range fails validity
    Accumulation --> Manipulation: frozen edge swept in killzone
    Accumulation --> StandDown: killzone ends, no sweep
    Manipulation --> Distribution: shift confirms after the sweep
    Manipulation --> StandDown: no shift before deadline
    Distribution --> StandDown: order sent - one per day
    StandDown --> [*]: new trading day resets
    
Every phase has two exits: forward on confirmation, sideways to stand-down on a missed deadline. The second exit is the one most builds forget.

Notice what the diagram makes obvious and prose tends to hide: every state has two exits. One moves the cycle forward on confirmation; one drops to stand-down because a window closed with nothing to show. Builds that only code the forward exits end up waiting all afternoon for a sweep that was only ever valid in the morning.

Here is the skeleton. Written in MQL5 for MetaTrader 5, but the shape ports to any platform that gives you a per-tick or per-bar callback.

mql5 amd_state.mqh
enum ENUM_AMD_PHASE
  {
   AMD_IDLE,           // accumulation window has not closed yet
   AMD_ACCUMULATION,   // a valid range is frozen, waiting for a sweep
   AMD_MANIPULATION,   // an edge was swept, waiting for the structure shift
   AMD_DISTRIBUTION,   // shift confirmed, the position is live
   AMD_STAND_DOWN      // nothing more to do until tomorrow
  };

ENUM_AMD_PHASE g_phase        = AMD_IDLE;
datetime       g_day          = 0;     // D1 bar time of the current trading day
double         g_rangeHigh    = 0.0;   // frozen once, at the end of accumulation
double         g_rangeLow     = 0.0;
double         g_manipExtreme = 0.0;   // keeps moving while in AMD_MANIPULATION
int            g_sweepBar     = -1;    // bar index of the sweep, for the shift search
int            g_bias         = 0;     // +1 bullish day, -1 bearish day
bool           g_tradedToday  = false;

void OnTick()
  {
   datetime today = iTime(_Symbol, PERIOD_D1, 0);
   if(today != g_day)                  // new server day: wipe yesterday
     {
      g_day = today;
      ResetDailyState();
     }

   if(!IsNewBar(InpWorkTF))            // decide on closed bars only
      return;

   switch(g_phase)
     {
      case AMD_IDLE:         TryOpenAccumulation(); break;
      case AMD_ACCUMULATION: TryManipulation();     break;
      case AMD_MANIPULATION: TryDistribution();     break;
      case AMD_DISTRIBUTION: ManageOpenTrade();     break;
      case AMD_STAND_DOWN:   break;
     }
  }
One switch, one phase per branch. Every rule you add later belongs inside one of those five functions, never inside OnTick.

Four details in that skeleton are worth more than they look.

The day boundary comes from the D1 bar, not from the clock. iTime(_Symbol, PERIOD_D1, 0) changes exactly when your broker's server rolls the day, which is what your session windows are already keyed to. Comparing local midnight instead is the fastest way to introduce clock drift between your reset and your killzones — and to have the EA reset itself in the middle of a live London session for anyone running a different broker time zone. If your accumulation window straddles the server rollover, offset from the D1 time rather than switching to local time.

Transitions are evaluated on closed bars. Checking a sweep mid-bar means the answer can change before the bar finishes; a wick pokes through, your phase flips, price closes back and the wick disappears from the chart. That is self-inflicted repainting, and it is the single most common reason a backtest and a live run disagree on which days traded.

ResetDailyState() clears everything, not just the phase. Range, extreme, direction, sweep bar, the traded flag. A half-cleared state is worse than no reset, because the EA looks like it is working.

Nothing outside the orchestrator writes g_phase. Detector functions return facts; only these five branches change state. Keep that discipline and you can unit-test each detector in isolation, which you will want to do long before you trust the whole assembly.

Detecting a Valid Accumulation Range (and Rejecting False Ones)

Here is the naive version everyone writes first: take the high and low of the accumulation window, call it the range, move on.

It works about as well as you would expect. On a night when price drifted 90 pips in one direction, the "range" is a trend channel — nothing later in the day will ever sweep it, so the EA sits in accumulation until the reset. On a dead pre-holiday night, the range is twelve pips wide, and normal spread widening at the London open sweeps both edges before any real participant has done anything. Both days get labelled accumulation by code that only measured a high and a low.

This is the failure that gets AMD indicators a bad name: they label any sideways chop as accumulation, and half of what they label is not sideways at all. Your state machine cannot fix it downstream. If the premise is wrong, the sweep is a coincidence and the structure shift is noise.

So AMD_IDLE → AMD_ACCUMULATION is not a time-based transition. It is a test, run once, when the window has fully closed:

  1. Size band, measured in ATR, not pips. A floor rejects ranges so tight that spread and a single news tick clear them; a ceiling rejects overnight trends wearing a range's clothes. Expressing both as multiples of daily ATR is what lets the same EA run on EUR/USD, gold and an index without three sets of hard-coded numbers.
  2. A drift test. A balanced range ends near where it started. Compare the last close in the window to the first, divide by the range height, and reject the day when that ratio is high — that is a market that trended quietly, not one that accumulated.
  3. Edge quality. Each edge should have been visited more than once. A single spike high that nothing came back to is not a level anyone is defending; it is an outlier, and using it as your sweep trigger means waiting for a price that may never return.
  4. A closed window. Freeze the range only after the last bar of the window has closed, and never touch g_rangeHigh / g_rangeLow again that day. Letting the range update as the manipulation leg develops is a genuinely nasty bug: the range chases price down, the "sweep" never registers, and the day silently produces nothing.
mql5 accumulation.mqh
// Called once, on the first closed bar after the accumulation window ends.
bool IsValidAccumulation(double hi, double lo, int bars, double atrD1)
  {
   double height = hi - lo;

   // 1. Size band in daily ATR, so the filter ports across symbols.
   if(height < InpMinRangeATR * atrD1) return(false);  // too tight: noise sweeps it
   if(height > InpMaxRangeATR * atrD1) return(false);  // too wide: this was a trend

   // 2. Drift test: balance ends near where it began.
   double first = iClose(_Symbol, InpWorkTF, bars);
   double last  = iClose(_Symbol, InpWorkTF, 1);
   if(MathAbs(last - first) / height > InpMaxDrift) return(false);

   // 3. Both edges must be real levels, not one lonely spike.
   if(TouchCount(hi, InpEdgeTol, bars) < 2) return(false);
   if(TouchCount(lo, InpEdgeTol, bars) < 2) return(false);

   return(true);
  }

void TryOpenAccumulation()
  {
   if(!AccumulationWindowClosed()) return;

   double hi = HighestHigh(InpWorkTF, AccumulationBars());
   double lo = LowestLow(InpWorkTF, AccumulationBars());

   if(!IsValidAccumulation(hi, lo, AccumulationBars(), ATR(PERIOD_D1)))
     { g_phase = AMD_STAND_DOWN; LogSkip("range failed validity"); return; }

   g_rangeHigh = hi;   // frozen for the rest of the day
   g_rangeLow  = lo;
   g_phase     = AMD_ACCUMULATION;
  }
The validity test runs once and is the EA's only defence against trading a premise that was never there.

Tune those four inputs on the instruments you actually trade, and resist the urge to loosen them because the EA is skipping too many days. A skipped day costs nothing. A day traded on a fake range costs a full stop.

Wiring the Manipulation Phase: Sweep Detection Inside the Right Session

This transition is where most builds either duplicate work a sibling module already does, or fire on noise. The fix is to be strict about what this layer owns.

It does not own sweep detection — the liquidity-sweep detection logic itself is its own problem, with its own definition of what counts as a run and a reclaim. It does not own the clock either: the killzone session windows the AMD cycle maps to belong to a separate module.

That module is your session filter; if you want the exact hours in your own broker's server time, a market-hours reference is a faster way to get them right than counting DST offsets by hand.

What the state machine owns is the conjunction and the ordering:

  • the phase must currently be AMD_ACCUMULATION;
  • server time must be inside the manipulation window;
  • the sweep must be of one of the two frozen edges — not of any level the detector finds attractive.

That last point is the composition rule that makes the whole architecture work. You pass the level in. A sweep detector left to find its own levels will happily report a run on an intraday high from twenty minutes ago, which has nothing to do with the day's cycle. Your orchestrator hands it a specific price and asks a specific question: was this level run and reclaimed, within this many bars?

Two more responsibilities sit here and nowhere else. The first is the deadline: if the manipulation window closes with neither edge swept, the day is over — go to stand-down rather than leaving the EA armed into the afternoon, where a sweep means something entirely different. The second is recording the extreme: from the moment the phase flips, track the furthest price reached against the day's direction. That single number becomes your stop, so it needs to keep updating until the structure shift confirms — unlike the range, which is frozen.

mql5 manipulation.mqh
void TryManipulation()
  {
   if(!InManipulationWindow(TimeCurrent()))
     {
      if(ManipulationWindowPassed(TimeCurrent()))
        { g_phase = AMD_STAND_DOWN; LogSkip("window closed, no sweep"); }
      return;
     }

   // Ask about OUR frozen edges. Never let the detector pick its own level.
   SweepResult ssl = DetectSweep(g_rangeLow,  SWEEP_BELOW, InpReclaimBars);
   SweepResult bsl = DetectSweep(g_rangeHigh, SWEEP_ABOVE, InpReclaimBars);

   if(ssl.found && bsl.found)          // both edges taken: no single thesis
     { g_phase = AMD_STAND_DOWN; LogSkip("both edges swept"); return; }

   if(ssl.found)      { g_bias = +1; g_manipExtreme = ssl.extreme; g_sweepBar = ssl.bar; }
   else if(bsl.found) { g_bias = -1; g_manipExtreme = bsl.extreme; g_sweepBar = bsl.bar; }
   else                 return;        // still inside the window, keep waiting

   g_phase = AMD_MANIPULATION;
  }
The orchestrator supplies the level and the deadline; the detector only answers whether that level was run and reclaimed.

Bullish Days vs Bearish Days: SSL and BSL Sweeps

An EA that only handles one direction trades roughly half the days it should, and the half it misses are not random — they cluster in whichever regime your test period happened to under-represent.

The mapping is symmetric, which is what makes it cheap to support both from one machine:

  • Sell-side liquidity taken — price runs below the frozen range low and reclaims. The day's expected distribution is upward: g_bias = +1.
  • Buy-side liquidity taken — price runs above the frozen range high and reclaims. The day's expected distribution is downward: g_bias = -1.

Set g_bias exactly once, at the transition, and have every later comparison read it rather than a hard-coded side. In practice that means writing your inequalities with the sign folded in — g_bias * (price - level) > 0 instead of two mirrored branches — because two mirrored branches is how a bug ships on the short side only and survives six months of testing on a bullish sample.

Then decide, explicitly, what happens when both edges get swept inside the window. Price takes the low, reverses, takes the high, and now two contradictory theses are on the table. Treating it as "first sweep wins" quietly turns your EA into a breakout-chaser on the exact days institutions are hunting both sides. Standing down is the honest default; if you want to allow a re-arm, make it a named input so the behaviour is visible in your test results rather than buried in the logic.

Confirming Distribution: Gating Entry on the Market Structure Shift

A sweep on its own is not a reversal. Plenty of days run a range edge and simply keep going — that is a breakout, and taking it as a manipulation is how an AMD bot ends up short at the low of a trend day.

So the last gate before any order is the market structure shift that confirms the manipulation-to-distribution turn. Detecting one — how you define the swing points, what counts as a break versus a change of character — is its own build, and this layer should not re-derive it. What this layer must impose is three constraints on which shift counts:

  1. It must be dated after the sweep. Pass g_sweepBar into the detection call as the search origin. This is the single most common composition bug in the whole pattern: a routine that scans all available history cheerfully returns a shift that happened at 04:00, before the manipulation existed, and your EA enters on a confirmation that predates its own premise.
  2. It must point against the sweep. A sell-side sweep needs an upward shift; a buy-side sweep needs a downward one. With g_bias already set, that is one signed comparison, not two branches.
  3. It must arrive before the distribution window's deadline. A shift printing near the close is real, but the leg it would confirm has no session left to run in. Let the deadline pass and go to stand-down.

Give the entry a confirmation candle rule if your shift definition is loose — a close beyond the shift level rather than a touch. It costs a few pips of entry and removes a category of intrabar false starts that no amount of tuning elsewhere will fix.

Placing the Trade: Stop, Target, and the One-Shot-Per-Day Rule

Once the phase flips to AMD_DISTRIBUTION, the state machine has one job left, and every part of it is decided by values you already stored.

The stop-loss goes beyond the manipulation extreme, plus a buffer for spread and a fraction of ATR. That price is not an arbitrary distance — it is the only level whose violation says the day's thesis was wrong. Price trading back through the sweep's extreme means the run was not a raid at all. A fixed 20-pip stop, by contrast, gets you stopped out of correct days and keeps you in wrong ones.

The take-profit is sized from the actual stop distance, not from a fixed pip number, because the stop distance varies enormously with how deep the manipulation ran. Pick one of two rules and keep it: a multiple of risk, or the opposing liquidity across the range. They disagree often; logging both while you develop tells you which one your instrument actually pays for.

If you are still calibrating what a given reward-to-risk ratio demands of your historical win rate, run the numbers before you commit the multiplier to code.

Position size falls out of risk per trade and that stop distance — never a fixed lot. A day with a shallow raid and a day with a deep one carry the same money risk only if the lot size moves inversely to the stop.

And the day gets exactly one trade. Set the flag when the order is sent, not when it fills and certainly not when it closes. A stop-out is not an invitation to re-enter: the premise that produced the setup has already been invalidated by definition.

mql5 distribution.mqh
void TryDistribution()
  {
   // The manipulation extreme keeps moving until the shift confirms.
   double px = (g_bias > 0) ? iLow(_Symbol, InpWorkTF, 1) : iHigh(_Symbol, InpWorkTF, 1);
   g_manipExtreme = (g_bias > 0) ? MathMin(g_manipExtreme, px)
                                 : MathMax(g_manipExtreme, px);

   if(DeadlinePassed(InpDistributionEnd))
     { g_phase = AMD_STAND_DOWN; LogSkip("no shift before deadline"); return; }

   if(!DetectMSS(g_sweepBar, g_bias))   // must be dated AFTER the sweep
      return;

   if(g_tradedToday)
     { g_phase = AMD_STAND_DOWN; return; }

   double entry = (g_bias > 0) ? Ask() : Bid();
   double buf   = InpStopBufferATR * ATR(InpWorkTF) + CurrentSpread();
   double sl    = g_manipExtreme - g_bias * buf;
   double tp    = entry + g_bias * InpRR * MathAbs(entry - sl);
   double lots  = LotsForRisk(InpRiskPercent, MathAbs(entry - sl));

   if(SendOrder(g_bias, lots, sl, tp, InpMagic))
     {
      g_tradedToday = true;   // set on SEND, not on fill, never on close
      g_phase       = AMD_DISTRIBUTION;
     }
  }
One signed bias variable makes the long and short paths the same six lines, so a bug cannot ship on one side only.

Two operational traps live in that flag. If the terminal restarts mid-session, g_tradedToday comes back as false and the EA will happily take a second trade on a day it already traded — rebuild the flag in OnInit() by scanning today's history and open positions for your magic number. And if you enter with a pending order rather than at market, give it an expiry at the distribution deadline; an unfilled limit that survives to tomorrow will fill on a day whose cycle never happened.

Here is the whole sequence on one day, with the values the state machine is carrying at each step.

A bullish Power of Three day, as the state machine sees it EUR/USD 15m

Illustrative day. The stop is not a fixed distance - it is the recorded manipulation extreme plus a buffer, which is why the lot size has to be computed from it.

Those prices are an illustration, not a result. Any automated strategy can lose, and the day-by-day variance of a one-trade-per-day model is wider than most builders expect — read our risk warning before running anything like this with real money.

When Should the EA Stand Down Instead of Forcing a Trade?

Most days, this EA should do nothing. That is not a defect to tune away; it is the model working. The cycle needs a genuine balance, a genuine raid on one side of it, and a genuine turn — and a large share of trading days fail at least one of those tests.

The practical failure of naive AMD automation is that it has no way to say no. It labels whatever it sees, so it trades every day, and the days it invents cost as much as the days it earns. Making the refusal explicit is what turns the warning into code.

Stand-down conditions: skip the day when any one is true

0 / 11

Checklist complete — you’re cleared to proceed.

Wire a news filter to the eighth item rather than hoping volatility filters catch it — a scheduled release inside the manipulation window produces a picture-perfect sweep-and-reclaim that has nothing to do with the daily cycle.

And log every stand-down with a reason code. This costs ten minutes and repays them permanently: after a few hundred days you have a histogram of why the EA sat out. An EA that skips most days might be correctly filtered or badly configured, and only the distribution of reasons tells you which. If nearly every skip reads "range failed validity", your ATR band is wrong, not the market.

Fitting the AMD State Machine Into the Full EA Build

It is worth being precise about what this layer adds, because a much simpler automation of the same three words already exists — a mechanical version that treats one candle as the range, any breach of its edge as the manipulation, and a few closes back inside as the confirmation. That build is genuinely easier to ship, and on some instruments it does something useful. It is also a different strategy wearing the same name.

Design decisionMechanical single-candle AMD EASession-mapped Power of Three EA
Range source The previous candle's high and low on one timeframe The accumulation window's high and low, frozen at window close
Range validity None - any prior candle becomes a range ATR size band, drift test, edge-touch count
Manipulation trigger Any breach of the candle edge, with an optional depth filter A run and reclaim of a frozen edge, passed in by the orchestrator
Session awareness None - the same rule runs at every hour of the day Each transition is gated to its own window with a deadline
Entry confirmation A set number of closes back inside the range A structure shift dated after the sweep and opposite to it
Daily structure Repeats per candle, so many setups per day One cycle per trading day, one trade maximum
Refusing a day Implicit - it simply waits for the next candle Explicit stand-down conditions, each logged with a reason
What it costs you Little - there is almost no state to manage A real state machine to debug, and far fewer trades to judge it on

That last row is not a throwaway. Sequencing buys you selectivity, and selectivity buys you a much smaller sample — which makes every evaluation question harder, not easier. Go in expecting that.

Structurally, the AMD machine is one module in a larger assembly, and it is the only one that holds state. Treat the others as pure functions: each takes explicit arguments (a level, a start bar, a window), returns a small struct of facts, and never reads or writes the phase. That single rule is what lets you swap in a better sweep definition next month without touching the sequencing, and it is the piece that most often goes missing when a build grows organically out of one big OnTick.

Two habits make the difference between a state machine you trust and one you tolerate. Log every transition with its timestamp, the phase before and after, and the value that triggered it. Then reconcile a handful of days against the chart by eye before you look at a single equity figure — if the machine labelled the wrong bars, the strategy tester output is measuring a strategy you did not write. And test each detector alone, on days where you already know the answer, before assembling them.

From here, the remaining pieces are the ones every build shares: the risk layer, the deployment story, and the validation work. Those belong to the full ICT-to-EA automation workflow rather than to this module. What you should now have is the part that workflow assumes and rarely explains — the layer that turns three working detectors into one machine that knows what day it is.

FAQ

Which timeframe should the phase state machine run on?

Run the transitions on the lowest timeframe whose closes you trust — 5m or 15m is typical — while sourcing the range from that same series and the ATR band from D1. The key is that all four phase checks share one working timeframe, because a machine that evaluates accumulation on 15m and sweeps on 1m will report transitions in an order that does not exist on either chart.

What happens to the state machine if a position is still open at the daily reset?

Separate the two. ResetDailyState() should clear the phase, the range, the bias and the traded flag, but it must not close or forget positions — trade management belongs to a routine keyed on your magic number, not on the phase. In practice the cleanest rule is to close or hand off the position at the distribution deadline, so the reset only ever runs on a flat book.

Can the same state machine trade the weekly Power of Three?

Yes, and it is mostly a matter of changing what the boundaries mean: the reset keys off the weekly bar, accumulation covers the early part of the week, and the sweep and shift are evaluated on a higher timeframe. The one thing that does not scale is the validity filter's ATR reference — a weekly range needs weekly ATR, not daily, or the size band rejects everything.

How do I stop the EA taking a second trade after MetaTrader restarts mid-day?

Rebuild the state in OnInit() instead of trusting the variables. Scan today's closed deals and open positions for your magic number; if either exists, set the traded flag and put the phase in stand-down. Persisting the phase to a file works too, but reconstructing it from the account's own record is harder to get wrong and needs no cleanup.

Does this approach work outside forex, on indices or crypto?

The sequencing logic ports without changes, because it is about order, not about an instrument. What does not port is the session map: a market that trades around the clock has no equivalent of an Asian accumulation window, so you have to define the phases against that market's own liquidity rhythm — and re-tune the ATR band, since the ratio of a typical range to daily ATR is not the same across asset classes.

Sources & Further Reading

Want to go deeper? These independent, authoritative sources shaped this guide — each one is worth reading in full:

Signalbots Cross-Market Desk

The Cross-Market Desk is the SignalBots editorial team for topics that span every market — platform connectors, copy trading, partnership and IB programs, and the general mechanics of trading automation. We research and write the guides that apply no matter what you trade.

More from this desk

Discussions 0

Leave a comment