public override void PlaceOrder(Order order)
 {
     var price = order.Side == OrderSideBuy ? this.CurrentRate.Ask : this.CurrentRate.Bid;
     var trailingSize = order.TrailingStop * 0.0001m;
     var trailingAmount = order.Side == OrderSideBuy ? price - trailingSize : price + trailingSize;
     this.currentTrade = new Trade
     {
         Side = order.Side,
         TrailingAmount = trailingAmount,
         Price = price,
         TrailingStop = order.TrailingStop,
         StopLoss = order.StopLoss,
         Time = order.Timestamp,
         Units = order.Units
     };
     Console.WriteLine("Order placed ...");
 }
 public override void UpdateTrade(Trade updatedTrade)
 {
     this.currentTrade.StopLoss = updatedTrade.StopLoss;
     this.currentTrade.TrailingAmount = updatedTrade.TrailingAmount;
     this.currentTrade.TrailingStop = updatedTrade.TrailingStop;
 }
        private void LiquidateTrade(Rate newRate, Rate accountCurrencyRate, Trade currentTrade, decimal referencePrice)
        {
            var gainLoss = 0m;
            if (currentTrade.Side == OrderSideBuy)
            {
                if ((newRate.Bid - referencePrice) > DebouncingLimit) return;

                gainLoss = referencePrice - currentTrade.Price;
            }
            else
            {
                if (referencePrice - newRate.Ask > DebouncingLimit) return;

                gainLoss = currentTrade.Price - referencePrice;
            }

            Console.WriteLine("Stop loss triggered=>Gain/Loss={0} pips", gainLoss / newRate.QuoteCurrency.GetPipFraction());
            var gainLossInQuoteCurrency = gainLoss * currentTrade.Units;
            var gainLossConversionRate = this.GetAccountCurrencyRate(newRate, accountCurrencyRate);
            this.accountInformation.Balance += gainLossInQuoteCurrency * gainLossConversionRate;
            Console.WriteLine("{1} - Balance = {0}", this.accountInformation.Balance, currentTrade.Time);
            this.trades.Remove(currentTrade);
        }
        public override Rate GetRate(string instrument)
        {
            var currentCandle = this.GetCurrentCandle(this.nbOfCalls);

            var rateValue = currentCandle.FullRange * (decimal)this.rateGenerator.NextDouble() + currentCandle.Low;

            var minuteFraction = (double)(this.nbOfCalls % this.ticksInPeriod) / this.ticksInPeriod;

            this.nbOfCalls++;

            this.CurrentRate = new Rate
            {
                Ask = rateValue,
                Bid = rateValue,
                Instrument = instrument,
                Time = currentCandle.Timestamp.AddMinutes(this.periodInMinutes * minuteFraction)
            };

            if (this.currentTrade == null)
            {
                return this.CurrentRate;
            }

            var amountToCompare = 0m;
            if (this.currentTrade.Side == OrderSideBuy)
            {
                if (this.currentTrade.TrailingStop > 0)
                {
                    var newTrailingAmount = this.CurrentRate.Bid - this.currentTrade.TrailingStop * 0.0001m;

                    this.currentTrade.TrailingAmount = newTrailingAmount >= this.currentTrade.TrailingAmount
                        ? newTrailingAmount
                        : this.currentTrade.TrailingAmount;
                    amountToCompare = this.currentTrade.TrailingAmount;
                }
                else
                {
                    this.currentTrade.TrailingAmount = 0;
                    amountToCompare = this.currentTrade.StopLoss;
                }
            }
            else
            {
                if (this.currentTrade.TrailingStop > 0)
                {
                    var newTrailingAmount = this.CurrentRate.Ask + this.currentTrade.TrailingStop * 0.0001m;
                    this.currentTrade.TrailingAmount = newTrailingAmount < this.currentTrade.TrailingAmount
                        ? newTrailingAmount
                        : this.currentTrade.TrailingAmount;
                    amountToCompare = this.currentTrade.TrailingAmount;
                }
                else
                {
                    this.currentTrade.TrailingAmount = 0;
                    amountToCompare = this.currentTrade.StopLoss;
                }
            }

            var gainLoss = 0m;
            if (this.currentTrade.Side == OrderSideBuy)
            {
                if (this.CurrentRate.Bid < amountToCompare)
                {
                    gainLoss = (this.currentTrade.Price - amountToCompare) / DolarsByPip;
                    Console.WriteLine("Stop loss triggered=>Gain/Loss={0}", gainLoss);
                    this.balancePips += gainLoss * this.currentTrade.Units * DolarsByPip;
                    Console.WriteLine("{1} - Balance = {0}", this.balancePips, this.currentTrade.Time);
                    this.currentTrade = null;
                }
            }
            else
            {
                if (this.CurrentRate.Ask > amountToCompare)
                {
                    gainLoss = (amountToCompare - this.currentTrade.Price) / DolarsByPip;
                    Console.WriteLine("Stop loss triggered=>Gain/Loss={0}", gainLoss);
                    this.balancePips += gainLoss * this.currentTrade.Units * DolarsByPip;
                    Console.WriteLine("{1} - Balance = {0}", this.balancePips, this.currentTrade.Time);
                    this.currentTrade = null;
                }
            }

            return this.CurrentRate;
        }
        public void UpdateTrade(Trade updatedTrade)
        {
            var trade = this.trades.FirstOrDefault(x => x.Id == updatedTrade.Id);
            if (trade == null)
            {
                throw new Exception($"No such trade with Id = {updatedTrade.Id}");
            }

            trade.StopLoss = updatedTrade.StopLoss;
            trade.TakeProfit = updatedTrade.TakeProfit;
            trade.TrailingStop = updatedTrade.TrailingStop;
        }
Example #6
0
        private bool ShouldSetStopLoss(Trade currentTrade, Rate currentRate)
        {
            if (currentTrade.StopLoss > 0)
            {
                return false;
            }

            var pipFraction = currentTrade.QuoteCurrency.GetPipFraction();
            decimal? spreadPips = (currentRate.Ask - currentRate.Bid) / (2 * pipFraction);
            var cushionDeltaPrice = (spreadPips + SlippagePips + MarginalGainPips) * pipFraction;
            switch (currentTrade.Side)
            {
                case OrderSideBuy:
                    return currentTrade.TrailingAmount >= currentTrade.Price + cushionDeltaPrice;
                default:
                    return currentTrade.TrailingAmount <= currentTrade.Price - cushionDeltaPrice;
            }
        }
Example #7
0
        private bool ShouldCloseTrade(Trade currentTrade)
        {
            if (currentTrade.TrailingStop > 0)
            {
                return false;
            }

            var currentCandle = this.candles.LastOrDefault();

            switch (currentTrade.Side)
            {
                case OrderSideBuy:
                {
                    var fastEmaLowValue = this.fastEmaLow.Values.FirstOrDefault();
                    return currentCandle.Close < fastEmaLowValue;
                }
                case OrderSideSell:
                {
                    var fastEmaHighValue = this.fastEmaHigh.Values.FirstOrDefault();
                    return currentCandle.Close > fastEmaHighValue;
                }
            }

            return true;
            //This should not happen, but in case it happens, it could be wise to close the trade or maybe send a notification
        }
Example #8
0
        /// <summary>
        ///     Should happen every minute because the check needs to be frequent,
        ///     but the candles are queried in the predefined timeframe
        /// </summary>
        public void CheckRate(Rate newRate)
        {
            Trace.CorrelationManager.ActivityId = Guid.NewGuid();
            validations.Clear();

            if (this.CurrentRate != null && newRate.Time < this.CurrentRate.Time)
            {
                //This is likely to happen in a backtest or practice scenario
                validations.AddErrorMessage("Rate timer is going backwards");
            }
            else
            {
                this.CurrentRate = newRate;
            }

            candleBuilder.AddRate(newRate);

            Trace.TraceInformation("New rate : {0}", JsonConvert.SerializeObject(newRate));

            if (!this.isbacktesting)
            {
                var systemTimeDiff = this.dateProvider.GetCurrentUtcDate() - newRate.Time.ToUniversalTime();
                if (systemTimeDiff.TotalMinutes >= MaxRateStaleTime)
                {
                    validations.AddErrorMessage("Price timer lagging behind current time");
                }
            }

            if (!this.ValidateIndicatorsState(newRate))
            {
                validations.AddErrorMessage("Incomplete indicator values");
            }

            if (this.tradingAdapter.HasOpenTrade(this.AccountId))
            {
                var currentTrade = this.tradingAdapter.GetOpenTrade(this.AccountId);

                //Not sure if doing this or just keep the trailing stop

                if (!validations.IsValid && currentTrade.StopLoss > 0)
                {
                    Trace.TraceInformation("Closing trade because of validation errors and open trade with stop loss");
                    //If the trade is open with stop loss, it is likely that we can close the trade with a profit
                    this.tradingAdapter.CloseTrade(this.AccountId, currentTrade.Id);
                    Trace.TraceError(validations.ToString());
                    return;
                }

                if (!validations.IsValid)
                {
                    Trace.TraceInformation("Exit early because of validation errors and open trade");
                    Trace.TraceError(validations.ToString());
                    return;
                }

                if (this.ShouldCloseTrade(currentTrade))
                {
                    Trace.TraceInformation("Closing trade");
                    this.tradingAdapter.CloseTrade(this.AccountId, currentTrade.Id);
                    return;
                }

                if (this.ShouldSetStopLoss(currentTrade, newRate))
                {
                    Trace.TraceInformation("Break even");
                    var updatedTrade = new Trade
                    {
                        Id = currentTrade.Id,
                        StopLoss = currentTrade.TrailingAmount,
                        TrailingStop = 0,
                        TakeProfit = 0,
                        AccountId = this.AccountId
                    };
                    this.tradingAdapter.UpdateTrade(updatedTrade);
                    return;
                }

                Trace.TraceInformation("Exit early because of open trade, trailing amount {0}", currentTrade.TrailingAmount);
                return;
            }

            if (!validations.IsValid)
            {
                Trace.TraceInformation("Exit early because of validation errors");
                Trace.TraceError(validations.ToString());
                return;
            }

            if (this.tradingAdapter.HasOpenOrder(this.AccountId))
            {
                Trace.TraceInformation("Open order");
                return;
            }

            if (!this.IsTradingDay())
            {
                Trace.TraceInformation("Non trading day");
                return;
            }

            if (!this.IsTradingTime())
            {
                Trace.TraceInformation("Non trading time");
                return;
            }

            var currentSpread = Math.Abs(newRate.Ask - newRate.Bid) * (1.00m / newRate.QuoteCurrency.GetPipFraction());
            if (currentSpread > MaxSpread)
            {
                Trace.TraceInformation($"Not enough liquidity, spread = {currentSpread}");
                return;
            }

            var currentAdxValue = this.adx.Values.LastOrDefault() * 100;
            if (currentAdxValue < AdxTrendLevel)
            {
                Trace.TraceInformation("ADX ({0}) below threshold ({1})", currentAdxValue, AdxTrendLevel);
                return;
            }

            //TODO: Set history to calculate adx direction in app config
            //Calculating adx trend with more than one candle back will cause to lost many chances to enter and will enter late
            var previousAdxValue = this.adx.Values.TakeLast(2).Skip(1).FirstOrDefault() * 100m;

            if (currentAdxValue <= previousAdxValue)
            {
                Trace.TraceInformation("ADX not increasing {0} => {1}", previousAdxValue, currentAdxValue);
                return;
            }

            if (this.CanGoLong(newRate))
            {
                this.PlaceOrder(OrderSideBuy, newRate);
                return;
            }

            if (this.CanGoShort(newRate))
            {
                this.PlaceOrder(OrderSideSell, newRate);
            }
        }
 public virtual void UpdateTrade(Trade updatedTrade)
 {
     var result = this.proxy.PatchTradeAsync(updatedTrade.AccountId,
         updatedTrade.Id,
         new Dictionary<string, string>
         {
             { "takeProfit", updatedTrade.TakeProfit.ToString(CultureInfo.InvariantCulture) },
             { "trailingStop", updatedTrade.TrailingStop.ToString(CultureInfo.InvariantCulture) },
             { "stopLoss", updatedTrade.StopLoss.ToString(CultureInfo.InvariantCulture) }
         }).Result;
 }