/// <summary>Watch for position closed</summary> protected override void OnPositionClosed(Position position) { if (PosMgr != null && position == PosMgr.Position) { PosMgr = null; } }
/// <summary>Called when new data is received</summary> public override void Step() { // Only on new candles Position = FindLivePosition(Position); if (!Instrument.NewCandle) { return; } if (Debugger.IsAttached) { Debugging.Dump(Instrument, high_res: 20.0, emas: new[] { 100 }); if (Instrument.Count == 230) { Debug.WriteLine("Break"); } } var sym = Instrument.Symbol; var mcs = Instrument.MedianCandleSize(-10, 0); var C0 = Instrument[0]; var C1 = Instrument[-1]; var C2 = Instrument[-2]; // One at a time if (Position == null) { TradeType tt; string msg; // Look for a reversal int reversal_direction; QuoteCurrency target_entry; if (!Instrument.IsCandlePattern(0, out reversal_direction, out target_entry)) { return; } var preceeding_trend = Instrument.MeasureTrend(-10, 0); var tt_confirmation = C1.Bullish ? TradeType.Buy : TradeType.Sell; const double EmaSlopeToMCS = 20; var ema = Bot.Indicators.ExponentialMovingAverage(Bot.MarketSeries.Close, 100); var ema_slope = ema.Result.FirstDerivative(ema.Result.Count - 1) * EmaSlopeToMCS / mcs; // If the EMA slope is flat, look for ranging if (Math.Abs(ema_slope) < 1.0) { // Side of the EMA. Must be significantly to one side of the EMA var dist = C1.Close - ema.Result.LastValue; if (Math.Abs(dist) < mcs) { return; } tt = C1.Close < ema.Result.LastValue ? TradeType.Buy : TradeType.Sell; msg = "{0} - ranging EMA. Slope = {1}, Distance = {2}".Fmt(tt, ema_slope, dist); } // Otherwise look for indecision near the EMA else { var dist = C1.Close - ema.Result.LastValue; if (Maths.Abs(dist) > mcs) { return; } tt = ema_slope > 0.0 ? TradeType.Buy : TradeType.Sell; msg = "{0} - trending EMA. Slope = {1}, Dist = {2}".Fmt(tt, ema_slope, dist); } // Check the candle confirms the direction if (tt != tt_confirmation) { return; } var sign = tt.Sign(); var rtr = new RangeF(1.0, 2.0); var trade = new Trade(Bot, Instrument, tt, Label, rtr_range: rtr); Bot.Print(msg); Position = Bot.Broker.CreateOrder(trade); PositionManager = new PositionManager(Instrument, Position); } else { PositionManager.Step(); } }
// Trade management // - Set SL past recent peaks / 00 levels // - Close trade if signals change /// <summary>Watch for pending order filled</summary> protected override void OnPositionOpened(Position position) { PosMgr = new PositionManagerNervious(this, position); }
/// <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 } }