/// <summary> /// Set the SL/TP on an existing position so that there's a chance it'll close in profit. /// Sets the SL to the wick limit of the latest and previous candle. /// This will only ever reduce the SL distance from it's current value. /// Set 'tp' to chance the current TP value as well (only if the SL is changed tho). /// Returns true if the SL was changed</summary> public bool CloseByStopLoss(Instrument instr, Position position, QuoteCurrency?tp = null) { // 'tp' is optional, it set it replaces the TP on 'position' Debug.Assert(instr.SymbolCode == position.SymbolCode); // Set the SL to just past the last candle var price = instr.CurrentPrice(-position.Sign()); var sl = position.Sign() > 0 ? Math.Min(instr[-1].WickLimit(-1), instr[0].WickLimit(-1)) - instr.Spread : Math.Max(instr[-1].WickLimit(+1), instr[0].WickLimit(+1)) + instr.Spread; // Only make the SL smaller var sl_rel = position.Sign() * (position.EntryPrice - sl); if (sl_rel > position.StopLossRel()) { return(false); } // Move the stop loss to just past the current price var trade = new Trade(instr, position) { SL = sl, TP = tp ?? position.TakeProfit }; if (!ModifyOrder(position, trade)) { ClosePosition(position, "CloseByStopLoss Failed"); // Something went wrong, just close now } return(true); }
///// <summary>Called when new data is received</summary> //public override void Step() //{ // var current_price = Instrument.CurrentPrice(0); // var sym = Instrument.Symbol; // // Find the stats for the recent price history // PriceStats.Add((double)current_price); // // Synchronise the Positions with our list // SynchroniseLivePositions(); // // Measure the current net position // var net = NetPosition; // // If the sum of all trades is greater than the threshold, cash in // // If the sum of all trades exceeds 90% of the balance to risk, bail out // var profit_threshold = Misc.Max(1.0, Broker.Balance * TakeProfitPC); // if (net > profit_threshold) // { // CloseOut(); // return; // Return so that closed positions get output while debugging // } // if (net < -BalanceRisked) // { // CloseOut(); // return; // Return so that closed positions get output while debugging // } // // Attempt to compress the positions // for (;Positions.Count > 2;) // { // var pos0 = Positions.Back(0); // var pos1 = Positions.Back(1); // if (pos0.NetProfit + pos1.NetProfit > 1.0) // { // Broker.ClosePosition(pos0); // Broker.ClosePosition(pos1); // continue; // } // break; // } // // If we have no position, make a trade // if (Positions.Count == 0) // { // // Get the current price direction // var q = Quadratic.FromPoints( // new v2(-2f, (float)Instrument[-2].Median), // new v2(-1f, (float)Instrument[-1].Median), // new v2(-0f, (float)Instrument[-0].Median)); // var next_price =(QuoteCurrency)q.F(1.0); // var tt = next_price > current_price ? TradeType.Buy : TradeType.Sell; // var sign = tt.Sign(); // // Create the initial trade // var volume = sym.VolumeMin; // var risk = Broker.MaxSL(Instrument, volume); // var ep = Instrument.CurrentPrice(sign); // var tp = ep + sign * risk; // var sl = ep - sign * risk; // var trade = new Trade(Instrument, tt, Label, ep, sl, tp, volume); // // Record how much we're risking on this set of trades // BalanceRisked = sym.QuoteToAcct(risk * volume); // // Open the position // var pos = Broker.CreateOrder(trade); // if (pos != null) // Positions.Add(pos); // } // else // { // // Get the loss threshold given the current number of trades // var loss_per_level = BalanceRisked / ReversesCount; // var loss_threshold = Positions.Count * loss_per_level; // // If the net profit has dropped below the threshold, open a reverse trade // if (net < -loss_threshold) // { // // The reverse trade is created such that the total risk remains the same. // var prev = Positions.Back(); // var volume = sym.NormalizeVolume(prev.Volume * (Positions.Count + 1) / Positions.Count); // var tt = prev.TradeType.Opposite(); // var ep = Instrument.CurrentPrice(tt.Sign()); // var tp = prev.StopLossAbs(); // var sl = prev.TakeProfitAbs(); // var trade = new Trade(Instrument, tt, Label, ep, sl, tp, volume); // // Open the reverse position // var pos = Broker.CreateOrder(trade); // if (pos != null) // Positions.Add(pos); // } // } // int break_point; // if (Instrument.NewCandle) // break_point = 1; //} ///// <summary>Update the collection 'Positions' with any live positions created by this strategy</summary> //private void SynchroniseLivePositions() //{ // var live_positions = Bot.Positions.ToHashSet(x => x.Id); // Positions.RemoveIf(x => !live_positions.Contains(x.Id)); //} /// <summary>Returns a trade when a likely good trade is identified. Or null</summary> private Trade FindEntry() { // Get the current price direction var q = Quadratic.FromPoints( new v2(-2f, (float)Instrument[-2].Close), new v2(-1f, (float)Instrument[-1].Close), new v2(-0f, (float)Instrument[-0].Close)); // Choose the trade type var curr_price = (double)Instrument.CurrentPrice(0); var next_price = q.F(1.0); var tt = next_price > curr_price ? TradeType.Buy : TradeType.Sell; var sign = tt.Sign(); { // Look for some confirming signals // Does it match the long period EMA? const double ema100_threshold_gradient = 0.1; // pips per index var ema100 = Bot.Indicators.ExponentialMovingAverage(Instrument.Data.Close, 100); var grad100 = ema100.Result.FirstDerivative() / Instrument.MCS; if (Math.Abs(grad100) < ema100_threshold_gradient || Math.Sign(grad100) != sign) { return(null); } // Does it match the short period EMA? const double ema14_threshold_gradient = 0.1f; // pips per index var ema14 = Bot.Indicators.ExponentialMovingAverage(Instrument.Data.Close, 14); var grad14 = ema14.Result.FirstDerivative() / Instrument.MCS; if (Math.Abs(grad14) < ema14_threshold_gradient || Math.Sign(grad14) != sign) { return(null); } // Is the price within the MCS of the long period EMA? if (Math.Abs(curr_price - ema100.Result.LastValue) > Instrument.MCS) { return(null); } // Is the instrument over-bought or over-sold var rsi = Bot.Indicators.RelativeStrengthIndex(Instrument.Data.Close, 14); if (tt == TradeType.Buy && rsi.Result.LastValue > 70.0) { return(null); // Over bought } if (tt == TradeType.Sell && rsi.Result.LastValue < 30.0) { return(null); // Over sold } // Has the current price just left a strong SnR level? // Is there a candle pattern that agrees with the trade } return(new Trade(Instrument, tt, Label)); }
/// <summary> /// Create a trade with automatic SL and TP levels set. /// SL/TP levels are set based on the current account balance (even if 'idx' != 0)</summary> /// <param name="instr">The instrument to be traded</param> /// <param name="tt">Whether to buy or sell</param> /// <param name="label">Optional. An identifying name for the trade</param> /// <param name="ep">Optional. The price at which the trade was entered. (default is current ask/bid price)</param> /// <param name="sl">Optional. The stop loss (absolute) to use instead of automatically finding one</param> /// <param name="tp">Optional. The take profit (absolute) to use instead of automatically finding one</param> /// <param name="risk">Optional. Scaling factor for the amount to risk. (default is 1.0)</param> /// <param name="comment">Optional. A comment/tag associated with the trade</param> /// <param name="idx">Optional. The instrument index of when the trade was created. (default is the current time)</param> public Trade(Instrument instr, TradeType tt, string label = null, QuoteCurrency?ep = null, QuoteCurrency?sl = null, QuoteCurrency?tp = null, double?risk = null, string comment = null, Idx?idx = null, EResult result = EResult.Unknown) : this(instr, tt, label, 0, null, null, 0, comment : comment, idx : idx, result : result) { try { var bot = instr.Bot; var sign = tt.Sign(); Idx index = idx ?? 0; bot.Debugging.Trace("Creating Trade (Index = {0})".Fmt(TradeIndex)); // If the index == 0, add the fractional index amount if (index == 0) { var ticks_elapsed = bot.UtcNow.Ticks - instr.Latest.Timestamp; var ticks_per_candle = instr.TimeFrame.ToTicks(); EntryIndex += Maths.Clamp((double)ticks_elapsed / ticks_per_candle, 0.0, 1.0); ExitIndex = EntryIndex; } // Set the trade entry price EP = ep ?? (index == 0 ? (QuoteCurrency)instr.CurrentPrice(sign) // Use the latest price : (QuoteCurrency)instr[index].Open + (sign > 0 ? instr.Symbol.Spread : 0)); // Use the open price of the candle at 'index' // Choose a risk scaler risk = risk ?? 1.0; // Find the account currency value of the available risk var balance_to_risk = bot.Broker.BalanceToRisk * risk.Value; if (balance_to_risk == 0) { throw new Exception("Insufficient available risk. Current Risk: {0}%, Maximum Risk: {1}%".Fmt(100 * bot.Broker.TotalRiskFrac, 100.0 * bot.Settings.MaxRiskFrac)); } // Require the SL to be at least 2 * the median candle size var volatility = instr.Symbol.QuoteToAcct(2 * instr.MCS * instr.Symbol.VolumeMin); if (balance_to_risk < volatility) { throw new Exception("Insufficient available risk. Volatility: {0}, Balance To Risk: {1}".Fmt(volatility, balance_to_risk)); } // Get the instrument to recommend trade exit conditions var exit = instr.ChooseTradeExit(tt, EP, idx: index, risk: risk); TP = tp != null ? tp.Value : exit.TP; SL = sl != null ? sl.Value : exit.SL; Volume = sl != null?instr.Bot.Broker.ChooseVolume(instr, sl.Value / risk.Value) : exit.Volume; } catch (Exception ex) { Error = ex; } }
/// <summary></summary> protected override void StepCore() { if (!Instrument.NewCandle) { return; } // Don't adjust the SL until the profit is better than MinRtR if (!MinRtRExceeded) { return; } // Look at the last few candles to find the level for the SL var range = Instrument.PriceRange(-NumCandles, 1); // Don't set the SL above the current price var sign = Position.Sign(); var min_sl = Instrument.CurrentPrice(-sign) - sign * Instrument.MCS; // Choose the worst over the range and adjust the SL if (Position.Sign() > 0) { var sl = range.Beg - 5 * Instrument.PipSize - Instrument.Spread; if (sl < min_sl && (sl > Position.StopLoss || Position.StopLoss == null)) { Broker.ModifyOrder(Instrument, Position, sl: sl); } } else { var sl = range.End + 5 * Instrument.PipSize + Instrument.Spread; if (sl > min_sl && (sl < Position.StopLoss || Position.StopLoss == null)) { Broker.ModifyOrder(Instrument, Position, sl: sl); } } }
/// <summary>Called when new data is received</summary> public override void Step() { base.Step(); if (Instrument.NewCandle) { Dump(); } var mcs = Instrument.MCS; var price = Instrument.LatestPrice; var trend_sign_slow = TrendSignSlow; var trend_sign_fast = TrendSignFast; // On a significantly extreme price level, buy/sell // For opening positions, use Bid when the price is high, Ask when the price is low. var sign = -ProbSign; var prob = Prob(sign); if (Math.Abs(prob) > OpenPositionProb) { // Todo: // The TrendSignFast is preventing trades being taken // If this worked it would offset the bad trades at MA crosses Dump(); // Buy the lows, Sell the highs var ep = Instrument.CurrentPrice(sign); var risk = Risk / 2; // Divide the risk amongst the number of positions that can be opened (1 in each direction) Debugging.Trace("Entry Trigger - {0} - prob={1:N3}".Fmt(sign > 0 ? "Buy" : "Sell", prob)); Debugging.Trace(" FastTrend = {0}, SlowTrend = {1}".Fmt(trend_sign_fast, trend_sign_slow)); for (;;) { // Only allow one trade in each direction using this method if (Positions.Count(x => x.Sign() == sign) != 0) { Debugging.Trace(" -Position with this sign already exists"); break; } Debugging.Trace(" +No existing {0} position".Fmt(sign)); // Only open positions in the direction of the trend if (sign == -trend_sign_fast) { Debugging.Trace(" -Against the trend ({0})".Fmt(trend_sign_fast)); break; } Debugging.Trace(" +Not against the trend ({0} vs. {1})".Fmt(sign, trend_sign_fast)); // Don't open positions too close together if (Broker.NearbyPositions(Label, ep, Instrument.MCS * MinTradeSeparation, sign)) { Debugging.Trace(" -Nearby trades with the same sign ({0})".Fmt(sign)); break; } Debugging.Trace(" +No nearby trades with the same sign ({0})".Fmt(sign)); // Only open positions when the recent price has been heading in the direction we want to trade var price_trend = Instrument.HighRes.FirstDerivative(-1) / Instrument.PipSize; if (Math.Sign(price_trend) == -sign) { Debugging.Trace(" -Price trend ({0:N3}) against trade sign ({1})".Fmt(price_trend, sign)); break; } Debugging.Trace(" +Price trend ({0:N3} pips/tick) matches trade sign ({1})".Fmt(price_trend, sign)); // Don't open a position if the MA's look like they're about to cross // in a direction that would make the trade against the trend. var next_cross_idx = NextCrossIndex; var ma_cross_sign = MA0[0].CompareTo(MA1[0]); if (next_cross_idx != null && next_cross_idx.Value < 2.5 && ma_cross_sign == sign) { Debugging.Trace(" -MA cross predicted in {0:N3} candles".Fmt(next_cross_idx.Value)); break; } Debugging.Trace(" +No MA cross from {0} to {1} predicted soon ({2})".Fmt(ma_cross_sign, -ma_cross_sign, next_cross_idx != null ? ((double)next_cross_idx.Value).ToString("N3") : ">5")); // Choose a SL based on SnR levels and the slow MA var rel = Math.Abs(ep - MA1[0]) + mcs; // Create the positions var tt = CAlgo.SignToTradeType(sign); var sl = ep - sign * rel; var tp = ep + sign * rel * 1.5; var vol = Broker.ChooseVolume(Instrument, rel, risk: risk); var trade = new Trade(Instrument, tt, Label, ep, sl, tp, vol, result: Trade.EResult.Open); //Debugging.LogTrade(trade); Broker.CreateOrder(trade); break; } } }
/// <summary>Set the SL or TP for a position such that it will close when the price hits 'price'</summary> public void CloseAt(Instrument instr, Position position, QuoteCurrency price) { Debug.Assert(instr.SymbolCode == position.SymbolCode); Bot.TradeStats.Event(position, "CloseAt"); var trade = (Trade)null; if (position.TradeType == TradeType.Buy) { // If the current price is not between 'price' and 'position.StopLoss' move the SL to 'price' if (!instr.CurrentPrice(-1).Within(position.StopLoss ?? -double.MaxValue, price)) { trade = new Trade(instr, position) { SL = price - instr.Spread } } ; // If the current price is not between 'price' and 'position.TakeProfit' move the TP to 'price' else if (!instr.CurrentPrice(+1).Within(price, position.TakeProfit ?? +double.MaxValue)) { trade = new Trade(instr, position) { TP = price } } ; // If the current price spans 'price' close immediately else { trade = null; } } else { // If the current price is not between 'price' and 'position.StopLoss' move the SL to 'price' if (!instr.CurrentPrice(+1).Within(price, position.StopLoss ?? +double.MaxValue)) { trade = new Trade(instr, position) { SL = price + instr.Spread } } ; // If the current price is not between 'price' and 'position.TakeProfit' move the TP to 'price' else if (!instr.CurrentPrice(-1).Within(position.TakeProfit ?? -double.MaxValue, price)) { trade = new Trade(instr, position) { TP = price } } ; // If the current price spans 'price' close immediately else { trade = null; } } // Update or close the trade if (trade != null) { // Move the SL or TP ModifyOrder(position, trade); } else { // Close immediately if the price spread spans 'price' ClosePosition(position, "CloseAt at current Price"); } }
/// <summary>Manage an open trade. This should be called on each tick</summary> protected override void StepCore() { // Move SL to break even when profit > BreakEvenThreshold (adjusted for Spread) var adjusted_profit = Position.NetProfit - Instrument.Symbol.QuoteToAcct(Instrument.Spread * Position.Volume); if (adjusted_profit > BreakEvenThreshold && Position.StopLossRel() > 0) { Bot.Debugging.Trace("Moving SL to break even - Profit ${0} > Threshold ${1}".Fmt(Position.NetProfit, BreakEvenThreshold)); var trade = new Trade(Instrument, Position) { SL = Position.EntryPrice + Position.Sign() * 2.0 * Instrument.Spread }; Broker.ModifyOrder(Position, trade); } // What about candle follow mode: // If the price is heading for the TP and the last tops/bottoms of the last few candles // have been steadily increasing/decreasing. Move the SL to just above/below the previous candle // A state machine for managing the position switch (State) { // In this state, the trade looks good. Keep it open. case EState.TradeLooksGood: #region { // Enter scalp mode when profit > scalp threshold if (Profit > ScalpThreshold) { Bot.Debugging.Trace("Entering scalp mode - Profit ${0} > Threshold ${1}".Fmt(Profit, ScalpThreshold)); State = EState.Scalp; } else if (CandlesSincePeak >= ColdFeetCount) { // It's been a while since a profit peak was set... Bot.Debugging.Trace("Getting nervous... no peaks for a while"); State = EState.LookForExitAfterNextPeak; } break; } #endregion case EState.Scalp: { // In this mode we've made a decent profit. var close = false; for (;;) { //// Close if the profit drops to fraction of the peak //const double ScalpFraction = 0.5; //if (Position.NetProfit < ScalpFraction * PeakProfit) //{ // close = true; // Debugging.Trace("Position closed - profit dropped by {0} - ${1}".Fmt(ScalpFraction, Position.NetProfit)); //} // Close if the closed candle is an indecision candle. if (Instrument.NewCandle && Instrument[-1].Type(Instrument.MCS).IsIndecision()) { Bot.Debugging.Trace("Closing position - indecision candle"); close = true; break; } // Close if the peak of the candle just closed is not better than the candle before's peak i.e. Not higher lows, or lower highs if (Instrument.NewCandle && Position.EntryTime < Instrument[-1].TimestampUTC && Instrument.SequentialPeaks(Position.Sign(), 0) < 1) { Bot.Debugging.Trace("Closing position - peaks oppose trend direction"); close = true; break; } // Close if the candle closes worse then the close of the -2 candle if (Instrument.NewCandle && Position.EntryTime < Instrument[-1].TimestampUTC && Math.Sign(Instrument[-1].Close - Instrument[-2].Close) != Position.Sign()) { Bot.Debugging.Trace("Closing position - candle close was worse that close of -2 candle"); close = true; break; } // Close if the profit is huge if (Position.NetProfit > 2.0 * ScalpThreshold && TicksSincePeak > 10) { Bot.Debugging.Trace("Closing position - huge spike detected!"); close = true; break; } // Don't close. break; } if (close) { Broker.ClosePosition(Position, "{0} - Scalp".Fmt(R <PositionManagerNervious> .NameOf)); State = EState.PositionClosed; } break; } case EState.TrailingSL: { // In this mode, we move the SL up to just above/below a recently closed candle if (Instrument.NewCandle) { var sign = Position.TradeType.Sign(); var limit = Instrument.CurrentPrice(-sign); foreach (var c in Instrument.CandleRange(-3, 0)) { limit = sign > 0 ? Math.Min(limit, c.Low) : Math.Max(limit, c.High); } // Move the SL if in the correct direction if (sign > 0 && limit > Position.StopLoss || sign < 0 && limit < Position.StopLoss) { var trade = new Trade(Instrument, Position) { SL = limit }; Broker.ModifyOrder(Position, trade); } } break; } case EState.LookForExitAfterNextPeak: { // In this state, the profit hit a peak a while ago, // and we're waiting for it to come back to that peak // level so we can bail out. if (NewPeak) { State = EState.CloseAtNextNonPeak; Bot.Debugging.Trace("Closing at next non peak"); } else if (CandlesSincePeak >= 2 * ColdFeetCount) { State = EState.BailIfProfitable; Bot.Debugging.Trace("Closing when profitable"); } break; } case EState.CloseAtNextNonPeak: { // In this state we're looking to exit the trade the // next time the profit peak is not set if (!NewPeak) { Broker.ClosePosition(Position, "{0} - Non-Peak".Fmt(R <PositionManagerNervious> .NameOf)); State = EState.PositionClosed; Bot.Debugging.Trace("Position closed - non-peak"); } break; } case EState.CloseAtNextProfitDrop: { // In this state we just want out with the minimum of loss. // Bail as soon as the profit decreases if (!IsGain) { Broker.ClosePosition(Position, "{0} - Next Profit Drop".Fmt(R <PositionManagerNervious> .NameOf)); State = EState.PositionClosed; Bot.Debugging.Trace("Position closed - profit dropped"); } break; } case EState.BailIfProfitable: { // In this state, it's been too long since the peak was last // hit, bail out if profitable if (NewPeak) { State = EState.CloseAtNextNonPeak; } else if (Position.NetProfit > 0) { Broker.ClosePosition(Position, "{0} - Bail With Profit".Fmt(R <PositionManagerNervious> .NameOf)); State = EState.PositionClosed; } else if (CandlesSinceProfitable > 4 * ColdFeetCount) { State = EState.CloseAtNextProfitDrop; Bot.Debugging.Trace("Closing at next drop in profit"); } break; } } }
/// <summary>Called when new data is received</summary> public override void Step() { base.Step(); if (Instrument.NewCandle) { Dump(); } // Don't open new positions while there are pending orders or existing positions if (Positions.Any() || PendingOrders.Any()) { return; } // Look for peak patterns TradeType tt; QuoteCurrency ep; var pat = Instrument.IsPeakPattern(Instrument.IdxNow, out tt, out ep); if (pat != null) { var pattern = pat.Value; Dump(); Debugging.Dump(new PricePeaks(Instrument, 0)); Debugging.Dump(new SnR(Instrument)); Debugging.Trace("Peak pattern: {0}".Fmt(pattern)); var sign = tt.Sign(); var mcs = Instrument.MCS; // Convert the patterns to trades switch (pattern) { case EPeakPattern.BreakOutHigh: case EPeakPattern.BreakOutLow: { // For break outs, enter immediately and use a candle // follow position manager because they tend to run. var price_range = Instrument.PriceRange(-10, 1); var sl = price_range.Mid - sign * price_range.Size * 0.6; var tp = (QuoteCurrency?)null; var vol = Broker.ChooseVolume(Instrument, Math.Abs(ep - sl), risk: Risk); var trade = new Trade(Instrument, tt, Label, ep, sl, tp, vol); var pos = Broker.CreateOrder(trade); if (pos != null) { PositionManagers.Add(new PositionManagerCandleFollow(this, pos, 5)); } break; } case EPeakPattern.HighReversal: case EPeakPattern.LowReversal: { // For reversals, enter when the price is near the trend line. // Use a fixed SL/TP var price_range = Instrument.PriceRange(-10, 1); var sl = price_range.Mid - sign * price_range.Size * 0.6; var tp = price_range.Mid + sign * price_range.Size * 0.4; var vol = Broker.ChooseVolume(Instrument, Math.Abs(ep - sl), risk: Risk); // If the current price is better than the entry price, enter immediately if (sign * (ep - Instrument.CurrentPrice(sign)) > 0) { var trade = new Trade(Instrument, tt, Label, ep, sl, tp, vol); Broker.CreateOrder(trade); } else { var order = new Trade(Instrument, tt, Label, ep, sl, tp, vol) { Expiration = Instrument.ExpirationTime(1) }; Broker.CreatePendingOrder(order); } break; } } } }
/// <summary>Called when new data is received</summary> public override void Step() { base.Step(); // Find the position associated with this strategy Position = FindLivePosition(Position); switch (State) { case EState.FindEntryTrigger: #region { // In this state we're waiting for an entry trigger. Debug.Assert(Position == null); // Do nothing unless the conditions are right if (SuitabilityScore < 0.5) { return; } if (Instrument.NewCandle) { Dump(); } // Always trade with the trend var sign = Math.Sign(Trend); var tt = sign > 0 ? TradeType.Buy : TradeType.Sell; // Look for a candle pattern that indicates entry int forecast_direction; QuoteCurrency target_entry; if (!Instrument.IsCandlePattern(0, out forecast_direction, out target_entry) || forecast_direction != sign) { return; } // Switch to the 'EnterOnPullBack' state State = EState.EnterOnPullBack; TargetEntry = new TargetEntryData(target_entry, tt); Dump(); break; } #endregion case EState.EnterOnPullBack: #region { if (Instrument.NewCandle) { Dump(); } // In this state we're waiting for the price to pull back // to the target entry. Check this on every tick. var tt = TargetEntry.TT; var cp = Instrument.CurrentPrice(tt.Sign()); if (Misc.Sign(cp - TargetEntry.Price) != tt.Sign()) { var trade = new Trade(Bot, Instrument, tt, Label); Position = Bot.Broker.CreateOrder(trade); Dump(); } break; } #endregion case EState.ManagePosition: #region { if (Instrument.NewCandle) { Dump(); } // Manage a trade position PositionManager.Step(); break; } #endregion } }