/// <summary>Strategy step</summary> protected override void Step() { var price = Instrument.LatestPrice; var mcs = Instrument.MCS; var slope = MACD.FirstDerivative(0) / Instrument.PipSize; // Hold a trade in the direction of the MACD slope // Use hysteresis to switch var position = Positions.FirstOrDefault(); if (position == null) { if (Math.Abs(slope) > SlopeThreshold) { var sign = Math.Sign(slope); var vol = Instrument.Symbol.VolumeMin; var trade = new Trade(Instrument, CAlgo.SignToTradeType(sign), Label, price.Price(sign), null, null, vol); Broker.CreateOrder(trade); } } else if (Instrument.NewCandle) { var sign = position.Sign(); if (Math.Abs(slope) < SlopeThreshold - Hysteresis) { Broker.ClosePosition(position, "{0} - Slope Changed".Fmt(R <Rylobot_MACD> .NameOf)); } } }
/// <summary>Increase the position size of winning trades</summary> private void IncreasePosition() { var mcs = Instrument.MCS; var price = Instrument.LatestPrice; foreach (var set in PositionSets.Values) { // Base position increases off the initial position // The initial position may be closed just before the other positions (hence null is possible) var pos0 = Positions.FirstOrDefault(x => x.Id == set[0]); if (pos0 == null) { continue; } var sign = pos0.Sign(); // Get the positions at are the increases var positions = Positions.Where(x => set.Contains(x.Id) && x.Sign() == sign).ToArray(); if (positions.Length >= MaxPositionsPerSet) { continue; } // Get the initial entry price var initial_ep = pos0.EntryPrice; // Get the level the price needs to have crossed var ep = initial_ep + sign * positions.Length * mcs * PositionIncreaseStep; // Add to the position every if 'ep' is crossed if (Math.Sign(price.Price(sign) - ep) == sign) { Dump(); // Adjust the SL on existing trades foreach (var pos in positions) { var adj_sl = pos.StopLoss + sign * mcs * PositionIncreaseStep; Broker.ModifyOrder(Instrument, pos, sl: adj_sl); } // Add to the position var sl = ep - sign * mcs * SLFrac; var vol = Broker.ChooseVolume(Instrument, mcs, risk: Risk / MaxPositionsPerSet); var trade = new Trade(Instrument, CAlgo.SignToTradeType(sign), Label, ep, sl, pos0.TakeProfit, vol, comment: pos0.Comment); Broker.CreateOrder(trade); } } }
/// <summary>Called when new data is received</summary> public override void Step() { base.Step(); if (Instrument.NewCandle || Positions.Any() || PendingOrders.Any()) { Dump(); } var sign0 = Math.Sign(EMA0[0].CompareTo(EMA1[0])); var sign1 = Math.Sign(EMA0[-1].CompareTo(EMA1[-1])); var order = PendingOrders.FirstOrDefault(); var position = Positions.FirstOrDefault(); // Cancel or close positions/orders that are against the trend if (order != null && order.Sign() != sign0) { Broker.CancelPendingOrder(order); order = null; } if (position != null && position.Sign() != sign0) { Broker.CloseAt(Instrument, position, EMA0[0]); } // Look for EMA crosses if (sign0 != sign1) { var mcs = Instrument.MCS; var price = EMA0[0] - sign0 * Instrument.Spread; if (order == null || Math.Abs(order.TargetPrice - price) > 2) { // Cancel any previous pending orders Broker.CancelAllPendingOrders(Label); // Create a pending order at the cross price var sl = price - sign0 * mcs * 5; var vol = Broker.ChooseVolume(Instrument, Math.Abs(price - sl), risk: Risk); var trade = new Trade(Instrument, CAlgo.SignToTradeType(sign0), Label, price, sl, null, vol); Broker.CreatePendingOrder(trade); } } }
protected override void OnPositionOpened(Position position) { // Prevent positions being entered on the same bulge EntryCooldown = NonIntersectingCount; { // Adjust the stop loss to allow for slip var mcs = Instrument.MCS; var sign = position.Sign(); var ep = position.EntryPrice; var sl = ep - sign * mcs * SLFrac; Broker.ModifyOrder(Instrument, position, sl: sl); } // If the main position of a position set opens, create a pending order in the opposite direction. // This is designed to catch the case when we choose a break-out trade but it's actually a reversal, or visa-versa. var id = Guid_.Parse(position.Comment); if (id != null && PositionSets[id.Value].Count == 1) { var mcs = Instrument.MCS; var sign = -position.Sign(); var tt = CAlgo.SignToTradeType(sign); var rel = Math.Abs(position.EntryPrice - position.StopLoss.Value) * 0.75; var ep = MA0[0] + sign * rel; var sl = ep - sign * mcs * SLFrac; 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, comment: Guid.NewGuid().ToString()) { Expiration = Instrument.ExpirationTime(2) }; Broker.CreatePendingOrder(trade); } // Close positions if there is a breakout in the wrong direction PositionManagers.Add(new PositionManagerBreakOut(this, position, CloseBreakoutPeriods, only_if_in_profit: true)); // Close positions when they fail to make new peaks PositionManagers.Add(new PositionManagerTopDrop(this, position, CloseTopDropCount, only_if_in_profit: true)); // Close positions when there's a steady stream of adverse candles PositionManagers.Add(new PositionManagerAdverseCandles(this, position, CloseAdverseCount)); }
/// <summary>Strategy step</summary> protected override void Step() { // Look for trade entry var sets_count = PositionSets.Count + PendingOrders.Count(); if (sets_count < MaxPositionSets && EntryCooldown == 0) { var mcs = Instrument.MCS; { var sign = -1; var ep = Donchian[0, Bot]; var tt = CAlgo.SignToTradeType(sign); var sl = ep - sign * mcs * SLFrac; 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, comment: Guid.NewGuid().ToString()) { Expiration = Instrument.ExpirationTime(1) }; Broker.CreatePendingOrder(trade); } { var sign = +1; var ep = Donchian[0, Top]; var tt = CAlgo.SignToTradeType(sign); var sl = ep - sign * mcs * SLFrac; 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, comment: Guid.NewGuid().ToString()) { Expiration = Instrument.ExpirationTime(1) }; Broker.CreatePendingOrder(trade); } EntryCooldown = 1; } }
/// <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; } } }
protected override void Step() { // Look for trade entry var sets_count = PositionSets.Count + PendingOrders.Count(); if (sets_count < MaxPositionSets && EntryCooldown == 0) { var mcs = Instrument.MCS; var price = Instrument.LatestPrice; var trend_sign = Math.Sign(MA0[0].CompareTo(MA1[0])); for (;;) { if (Math.Abs(MA1[0] - MA0[0]) < 0.5 * mcs) { break; } { var sign = trend_sign; var ep = MA1[0]; var tt = CAlgo.SignToTradeType(sign); var sl = ep - sign * mcs * SLFrac; var tp = MA0[0]; //(QuoteCurrency?)null; var vol = Broker.ChooseVolume(Instrument, Math.Abs(ep - sl), risk: Risk); var trade = new Trade(Instrument, tt, Label, ep, sl, tp, vol) { Expiration = Instrument.ExpirationTime(1) }; Broker.CreatePendingOrder(trade); break; } } //var dist_ask = MA0[0] - price.Ask; //var dist_bid = price.Bid - MA0[0]; //var trend0 = ((Monic)MA0.Extrapolate(1, 5).Curve).A; //var trend1 = Instrument.EMASlope(0); // //((Monic)MA1.Extrapolate(1, 5).Curve).A; //// Bias the price distance from the MA by the trend //dist_ask += trend0 * TrendWeight; //dist_bid -= trend0 * TrendWeight; //if (dist_ask > OpenDistance*mcs && Math.Sign(trend1) > 0) //{ // var sign = +1; // var ep = price.Ask; // var tt = TradeType.Buy; // var sl = ep - sign * mcs * SLFrac; // var tp = (QuoteCurrency?)null; // var vol = Broker.ChooseVolume(Instrument, Math.Abs(ep - sl), risk:Risk); // var trade = new Trade(Instrument, tt, Label, price.Ask, sl, tp, vol); // Broker.CreateOrder(trade); //} //if (dist_bid > OpenDistance*mcs && Math.Sign(trend1) < 0) //{ // var sign = -1; // var ep = price.Bid; // var tt = TradeType.Sell; // var sl = ep - sign * mcs * SLFrac; // var tp = (QuoteCurrency?)null; // var vol = Broker.ChooseVolume(Instrument, Math.Abs(ep - sl), risk:Risk); // var trade = new Trade(Instrument, tt, Label, price.Bid, sl, tp, vol); // Broker.CreateOrder(trade); //} } // Break point helper if (Instrument.NewCandle) { Dump(); } }
protected override void Step() { if (!Instrument.NewCandle) { return; } // Look for trade entry var sets_count = PositionSets.Count + PendingOrders.Count(); if (sets_count < MaxPositionSets && EntryCooldown == 0) { // Wait for a Doji candle var mcs = Instrument.MCS; var A = Instrument[-1]; var a_type = A.Type(mcs); if (!a_type.IsIndecision()) { return; } // Look for strong trade direction indications var trade_sign = (int?)null; for (;;) { // Divergent extrapolation var q0 = (Quadratic)MA0.Future.Curve; var q1 = (Quadratic)MA1.Future.Curve; var intersect = Maths.Intersection(q0, q1); if (Math.Sign(q0.A) != Math.Sign(q1.A) || intersect.Length == 0) // || intersect.FirstOrDefault() >= 0) { break; } trade_sign = Math.Sign(q0.A); break; } // Look for Marubozu followed by doji for (;;) { // Find the preceding non-doji var i = -2; var B = Instrument[i]; var b_type = B.Type(mcs); for (; i > Instrument.IdxFirst && b_type.IsIndecision(); --i, B = Instrument[i], b_type = B.Type(mcs)) { } if (!b_type.IsTrend()) { break; } { var sign = trade_sign ?? -B.Sign; var ep = A.Close; var tt = CAlgo.SignToTradeType(sign); var sl = ep - sign * mcs * SLFrac; var tp = ep + sign * mcs * SLFrac * 4; //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) { Expiration = Instrument.ExpirationTime(1) }; Broker.CreateOrder(trade); Debugging.Trace(" -Doji followed by Marubozu"); return; } } // Look for a strong candle trend followed by a hammer or inverted hammer for (;;) { // Get the candle trend leading up to 'A' var candle_trend = Instrument.MeasureTrendFromCandles(-3, -1); if (Math.Abs(candle_trend) < 0.8) { break; } // Look for a hammer pattern if (a_type == Candle.EType.Hammer && Math.Sign(candle_trend) > 0) { break; } if (a_type == Candle.EType.InvHammer && Math.Sign(candle_trend) < 0) { break; } { var sign = trade_sign ?? -Math.Sign(candle_trend); var ep = A.Close; var tt = CAlgo.SignToTradeType(sign); var sl = ep - sign * mcs * SLFrac; var tp = ep + sign * mcs * SLFrac * 4; //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) { Expiration = Instrument.ExpirationTime(1) }; Broker.CreateOrder(trade); Debugging.Trace(" -{0} pattern".Fmt(a_type)); return; } } //var trend_sign = Math.Sign(MA0[0].CompareTo(MA1[0])); //var price_range = Instrument.PriceRange(i, 1).Inflate(1.05); } }
/// <summary>Strategy step</summary> protected override void Step() { // MCS = 20-period EMA of the true range. // 1-Unit = Volume for a 1xMCS move equalling 1% equity // N-period break-out = the price exceeding the high or low of the last N periods. // N-period exit = the price making a new N-period low (for long positions) or high (for short positions) // System 1: // Enter on 20-period break-out if the prior break-out was not a winner. // Exit on 10-period break-out against the position. // Open 1 unit at entry, and add 1 unit every 0.5*MCS above entry, move all stops to 2*MCS below var price = Instrument.LatestPrice; var mcs = Instrument.MCS; var positions = Positions.ToArray(); if (positions.Length == 0) { // System 1 - look for a short period breakout var tt = Instrument.IsBreakOut(System1_Entry); if (tt != null) { var sign = tt.Value.Sign(); var ep = price.Price(sign); var sl = ep - sign * mcs * StopLossFrac; var vol = Broker.ChooseVolume(Instrument, 1 * mcs, risk: 1.0 / MaxPositions); var trade = new Trade(Instrument, tt.Value, Label, ep, sl, null, vol); // Enter if the last breakout was not a winner // var last = BreakoutsHistory.LastOrDefault(); // if (last == null || last.NetProfit < 0) { Dump(); Broker.CreateOrder(trade); ActiveBreakout = trade; } // else if (ActiveBreakout == null) // { // Dump(); // // // Save each breakout, so we can tell if they are winners // ActiveBreakout = trade; // } } } // Look to increase the position size else if (positions.Length > 0 && positions.Length < MaxPositions) { var sign = positions[0].Sign(); // Get the initial entry price var initial_ep = sign > 0 ? positions.Min(x => x.EntryPrice) : positions.Max(x => x.EntryPrice); // Get the level the price needs to have crossed var ep = initial_ep + sign * positions.Length * mcs * AddPositionFrac; // Add to the position every AddPositionFrac*N gain if (Math.Sign(Instrument.LatestPrice.Price(sign) - ep) == sign) { Dump(); var sl = ep - sign * mcs * StopLossFrac; var vol = Broker.ChooseVolume(Instrument, 1 * mcs, risk: 1.0 / MaxPositions); // Adjust the SL on existing trades foreach (var pos in positions) { Broker.ModifyOrder(Instrument, pos, sl: pos.StopLoss + sign * mcs * AddPositionFrac); } // Add to the position var trade = new Trade(Instrument, CAlgo.SignToTradeType(sign), Label, ep, sl, null, vol); Broker.CreateOrder(trade); } } // Simulate each active 'trade' if (ActiveBreakout != null) { ActiveBreakout.Simulate(Instrument.LatestPrice); // Look for exits on the simulated trade var exit_tt = Instrument.IsBreakOut(System1_Exit); if (exit_tt != null && ActiveBreakout.TradeType != exit_tt.Value) { Dump(); // Close any trades where 'exit_tt' is opposes the trade direction ActiveBreakout.Close(); BreakoutsHistory.Add(ActiveBreakout); ActiveBreakout = null; } } }
protected override void Step() { // Look for trade entry var sets_count = PositionSets.Count + PendingOrders.Count(); if (sets_count < MaxPositionSets && EntryCooldown == 0) { // Wait for a sequence of candles entirely above or below the MA var price = Instrument.LatestPrice; var mcs = Instrument.MCS; // Look for a sequence of candles that are entirely above or below the MA var bulge = FindBulges(0, MA0).FirstOrDefault(); if (bulge.Sign != 0 && Instrument.IdxLast - bulge.Range.End <= NonIntersectingCount) { Debugging.AreaOfInterest(bulge.Range, append: false); // Decide the direction int sign = 0; //// Trade in the direction of the slow MA if it is trending strongly //var ma_slope = MA1.FirstDerivative(0) / Instrument.PipSize; //if (Math.Abs(ma_slope) > MATrendSlope) //{ // sign = Math.Sign(ma_slope); //} //else { // Using measured stats of bulge sequences, the probabilities are: // 0 = below the MA, 1 = above the MA var next_bulge_sign = new [] { -1, // 000: -0.168091168091168 (count=351) +1, // 001: 0.224489795918367 (count=245) -1, // 010: -0.069767441860465 (count=172) +1, // 011: 0.158730158730159 (count=252) -1, // 100: -0.195121951219512 (count=246) +1, // 101: 0.139664804469274 (count=179) -1, // 110: -0.217391304347826 (count=253) +1, // 111: 0.064102564102564 (count=312) }; // Include the current bulge because the trade triggers when this bulge closes var bulge_signs = new List <int>(); FindBulges(0, MA0).Take(3).ForEach(b => bulge_signs.Insert(0, b.Sign)); // Careful with order sign = next_bulge_sign[Bit.SignsToIndex(bulge_signs)]; } { // Create a pending order var ep = MA0[0]; var tt = CAlgo.SignToTradeType(sign); var sl = ep - sign * mcs * SLFrac; // Note: the SL needs to be big enough that a paired order is triggered before this trade is closed 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, comment: Guid.NewGuid().ToString()) { Expiration = Instrument.ExpirationTime(1) }; Broker.CreatePendingOrder(trade); } EntryCooldown = 1; } } // Increase position size on winning positions //IncreasePosition(); // Break point helper if (Instrument.NewCandle) { Dump(); } }
/// <summary>Called when new data is received</summary> public override void Step() { base.Step(); using (Broker.SuspendRiskCheck(ignore_risk: true)) { const double TakeProfitFrac = 1.020; const double ReverseFrac = 0.998; Dump(); // The unit of volume to trade var mcs = Instrument.MCS; var ep = Instrument.LatestPrice.Mid; var vol = Broker.ChooseVolume(Instrument, 5.0 * mcs); // Open a trade in the direction of the trend if (ProfitSign == 0) { Dump(); Debugging.Trace("Idx={0},Tick={1} - Setting profit sign ({2}) - Equity = ${3}".Fmt(Instrument.Count, Bot.TickNumber, TrendSign, Equity)); var tt = CAlgo.SignToTradeType(TrendSign); var trade = new Trade(Instrument, tt, Label, ep, null, null, vol); Broker.CreateOrder(trade); LastEquity = Equity; } // While the profit direction is the same as the trend direction // look for good spots to add hedged pending orders if (ProfitSign == TrendSign) { // If the current price is at a SnR level, add pending orders on either side. // Hopefully price will reverse at the SnR level and only trigger one of the pending orders. // If price then reverses we can sell an existing profitable trade and be left with the // profit direction going in the right direction var snr = new SnR(Instrument, ep); var lvl_above = snr.Nearest(ep, +1, min_dist: 0.5 * mcs, min_strength: 0.7); var lvl_below = snr.Nearest(ep, -1, min_dist: 0.5 * mcs, min_strength: 0.7); // Choose price levels for the pending orders var above = lvl_above != null ? lvl_above.Price + 0.5 * mcs : ep + mcs; var below = lvl_below != null ? lvl_below.Price - 0.5 * mcs : ep - mcs; // Only recreate if necessary if (!PendingOrders.Any(x => x.TradeType == TradeType.Buy && Maths.FEql(x.TargetPrice, above, 5 * Instrument.PipSize)) || !PendingOrders.Any(x => x.TradeType == TradeType.Sell && Maths.FEql(x.TargetPrice, below, 5 * Instrument.PipSize))) { Dump(); Debugging.Dump(snr); Debugging.Trace("Idx={0},Tick={1} - Adjusting pending orders - Equity = ${2}".Fmt(Instrument.Count, Bot.TickNumber, Equity)); // Cancel any other pending orders further away than these too Broker.CancelAllPendingOrders(Label); var buy = new Trade(Instrument, TradeType.Buy, Label, above, null, null, vol); var sel = new Trade(Instrument, TradeType.Sell, Label, below, null, null, vol); Broker.CreatePendingOrder(buy); Broker.CreatePendingOrder(sel); } } // If the profit is against the Trend, do nothing until losing a fraction of last equity if (ProfitSign != TrendSign && Equity < ReverseFrac * LastEquity) { Dump(); Debugging.Trace("Idx={0},Tick={1} - Changing profit sign to ({2}) - Equity = ${3}".Fmt(Instrument.Count, Bot.TickNumber, TrendSign, Equity)); var sign = -ProfitSign; // Try to reverse the sign by closing profitable positions foreach (var pos in Positions.Where(x => x.Sign() != sign && x.NetProfit > 0).OrderBy(x => - x.NetProfit).ToArray()) { Broker.ClosePosition(pos); // Break out when the profit direction has been reversed if (ProfitSign == sign) { break; } } // Couldn't reverse the sign by closing positions, open a new one if (ProfitSign != sign) { var tt = CAlgo.SignToTradeType(sign); var reverse_vol = Math.Abs(NetVolume) + vol; var trade = new Trade(Instrument, tt, Label, ep, null, null, reverse_vol); Broker.CreateOrder(trade); } LastEquity = Equity; } // If equity is greater than a threshold, take profits if (Equity > TakeProfitFrac * LastEquity) { Dump(); Debugging.Trace("Idx={0},Tick={1} - Profit taking - Equity = ${2}".Fmt(Instrument.Count, Bot.TickNumber, Equity)); var sign = ProfitSign; foreach (var pos in Positions.Where(x => x.Sign() == sign && x.NetProfit > 0).OrderBy(x => - x.NetProfit).ToArray()) { if (pos.Volume >= Math.Abs(NetVolume)) { continue; } Broker.ClosePosition(pos); } LastEquity = Equity; } } }