Exemple #1
0
        /// <summary>Called when new data is received</summary>
        public override void Step()
        {
            base.Step();
            if (!Instrument.NewCandle)
            {
                return;
            }

            var position = Positions.FirstOrDefault();

            if (position == null)
            {
                var snr = new SnR(Instrument, tf: TimeFrame.Hour8);
                var mcs = Instrument.MCS;

                // Look for price being near an SnR level
                var price = Instrument.LatestPrice;
                var near  = snr.Nearest(price.Mid, 0);
                if (near == null || Math.Abs(near.Price - price.Mid) > 0.5 * mcs)
                {
                    return;
                }

                // Get the preceding trend
                var trend = Instrument.MeasureTrendFromCandles(-5, 1);
            }
        }
        /// <summary></summary>
        protected override void StepCore()
        {
            // If the SL is on the winning side, let the trade run its course
            var sl_rel = Position.StopLossRel();

            if (sl_rel < 0)
            {
                return;
            }

            var mcs             = Instrument.MCS;
            var sign            = Position.Sign();
            var price           = Instrument.LatestPrice;
            var trend_sign_slow = Strategy.TrendSignSlow;
            var trend_sign_fast = Strategy.TrendSignFast;

            // If the position opposes the slow trend, try to close it at break even
            if (sign == -trend_sign_slow && sign == -trend_sign_fast)
            {
                var cp = price.Price(-sign);

                // Choose a SL based on SnR levels
                var snr  = new SnR(Instrument); Debugging.Dump(snr);
                var near = snr.Nearest(cp, -sign, min_dist: mcs * 0.5);
                var sl   = near != null ? near.Price : cp - sign * mcs;
                if (Math.Abs(sl - Position.EntryPrice) < sl_rel)
                {
                    Broker.ModifyOrder(Instrument, Position, sl: sl);
                    Debugging.Trace("  +Position opposes the trend (trend={0}), closing at break even".Fmt(trend_sign_slow));
                    return;
                }
            }

            // If the trade is better than RtR=1:1, and the close probability is high close the trade
            // For closing positions, use Ask when the price is high, Bit when the price is low
            var prob_sign = Strategy.ProbSign;
            var prob      = Strategy.Prob(prob_sign);

            if (sign == prob_sign && Math.Abs(prob) > StrategyPriceDistribution.ClosePositionProb)
            {
                // If the price gradient is in the direction of the trade, don't close it
                var d = Instrument.HighRes.FirstDerivative(-1);
                if (Math.Sign(d) == sign)
                {
                    return;
                }

                var min_profit = Instrument.Symbol.QuoteToAcct(sl_rel * Position.Volume);
                if (Position.NetProfit > min_profit)
                {
                    Debugging.Trace("  +Close Prob: {0}".Fmt(prob));
                    Broker.ClosePosition(Position);
                }
            }
        }
        /// <summary>
        /// Update the 'Features' vector with values for the quality of a trade at 'CurrentIndex'.
        /// Note: args.Candle is the candle at 'CurrentIndex'</summary>
        protected override void UpdateFeatureValues(DataEventArgs args)
        {
            // Only make predictions on new candle events
            if (!args.NewCandle)
            {
                return;
            }

            //// Get the candle in question
            //var candle = Instrument[neg_idx];

            //// Assume 'candle' has just opened, so the current price is the Open price
            //var price = candle.Open;

            //// Get the average candle size over the history window
            //var mcs = Instrument.MeanCandleSize(neg_idx - HistoryWindow, neg_idx);

            Features.Clear();
            RSIFeatures();
            CandlePatterns();
#if false
            //{// Preceding candle data
            //	foreach (var c in Instrument.CandleRange(neg_idx - 3, neg_idx))
            //	{
            //		values.Add(c.Open );
            //		values.Add(c.High );
            //		values.Add(c.Low  );
            //		values.Add(c.Close);
            //	}
            //}
            {            // Preceding trend
                var trend = Instrument.MeasureTrend(neg_idx - 5, neg_idx);
                values.Add(trend);
            }
            {            // SnR Levels
                // Normalised Distance from the nearest SnR level.
                // 'Near' means within the mean candle size of the SnR level
                var snr = new SnR(Instrument, neg_idx - HistoryWindow, neg_idx);
                var lvl = snr.SnRLevels.MinBy(x => Math.Abs(price - x.Price));

                // Normalised distance based on the mean candle size with the candle centred on the SnR level
                var d    = Math.Abs(lvl.Price - price);
                var dist = Math.Min(2 * d / mcs, 1.0);
                values.Add(dist);
            }

            {            // EMA gradients
                var ema = new[]
                {
                    Bot.Indicators.ExponentialMovingAverage(Bot.MarketSeries.Close, 21),
                    Bot.Indicators.ExponentialMovingAverage(Bot.MarketSeries.Close, 55),
                    Bot.Indicators.ExponentialMovingAverage(Bot.MarketSeries.Close, 144),
                };
                for (var j = 0; j != ema.Length; ++j)
                {
                    for (var i = 3; i-- != 0;)
                    {
                        var index = neg_idx - i - Instrument.FirstIdx;
                        var dp    = ema[j].Result.FirstDerivative(index) / mcs;
                        var ddp   = ema[j].Result.SecondDerivative(index) / mcs;
                        values.Add(dp);
                        values.Add(ddp);
                    }
                }
            }
            {            // MACD
                // At Buy points:
                //  - MACD (blue line) is negative
                //  - MACD is a minima
                //  - The histogram has a positive gradient (opposite to the MACD)
                //  - The gap between MACD and the signal line (red line) is large
                // At Sell points:
                //  - inverse of buy
                var macd = Bot.Indicators.MacdCrossOver(Bot.MarketSeries.Close, 26, 12, 9);
                values.Add(macd.MACD.LastValue / mcs);
                values.Add(macd.Histogram.LastValue / mcs);

                var index = neg_idx - 1 - Instrument.FirstIdx;
                var y1    = macd.MACD.FirstDerivative(index) / mcs;
                var y2    = macd.MACD.SecondDerivative(index) / mcs;
                values.Add(y1);
                values.Add(y2);

                var h1 = macd.Histogram.FirstDerivative(index) / mcs;
                var h2 = macd.Histogram.SecondDerivative(index) / mcs;
                values.Add(h1);
                values.Add(h2);
            }
            {            // Bollinger Bands
                // Buy signal when:
                //  - price touches the centre line during a rising trend
                // Sell signal when:
                //  - price touches the upper band during a falling trend
                // Also, the distance between upper/lower bands means something
                var bb = Bot.Indicators.BollingerBands(Bot.MarketSeries.Close, 21, 2, MovingAverageType.Exponential);

                var index = neg_idx - 1 - Instrument.FirstIdx;
                var c1    = bb.Main.FirstDerivative(index) / mcs;
                values.Add(c1);

                // Signed distance from the upper/lower bands. Positive means outside the bands
                var dist_upper = (price - bb.Top.LastValue) / (bb.Top.LastValue - bb.Main.LastValue);
                var dist_lower = (bb.Bottom.LastValue - price) / (bb.Main.LastValue - bb.Bottom.LastValue);
                values.Add(dist_upper);
                values.Add(dist_lower);

                var dist = (bb.Top.LastValue - bb.Bottom.LastValue) / mcs;
                values.Add(dist);
            }
            return(values);
#endif
        }
Exemple #4
0
        /// <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;
                }
            }
        }