Replies

erik.lindblad
19 Oct 2020, 23:41

Hi Panagiotis!

I stumbled upon this post and the The ZigZag indicator that you are referring to is exactly what I am looking for. However, there is one problem that I am having with all custom indicators I have experimented with so far. Maybe I am misunderstanding the purpose of the indicators but I do not care about lines or objects being drawn on the charts, I want the indicator calculate something for me and to pass that data to my cBot so it can be used in the trading.

How am I supposed to do this? It looks that an indicator can only output properties of type IndicatorDataSeries, which seems to be just a list of doubles. I would like the indicator to expose just a simple list of custom objects representing the swing points. I tried to put the swing point objects in a public property inside the indicator and access them like shown in simplified pseudo below, but it does not work.

public class ZigZagIndicator : Indicator
{
    public List<SwingPoint> SwingPoints;

    public override void Calculate(int index)
    {
        //Calculate swing points and add them to my swing points list
        // Do not care about drawing anything on the chart
    }
}

public class SwingPoint
{
    public string Type { get; set; }
    public double Price { get; set; }
    // Maybe something more here, like date or index or something
}

public class Bot : Robot
{
    private ZigZagIndicator zigZagIndicator;

    protected override void OnStart()
    {
        zigZagIndicator = Indicators.GetIndicator<ZigZagIndicator>();
    }

    protected void OnBar()
    {
        var swingPoints = zigZagIndicator.SwingPoints; // Does not work, always empty
    }
}

Maybe I do not even need an indicator if I don't care about drawing anything on the chart? Maybe the bot can find the swing points itself? However, I do not fully understand the ZigZag algorithm so I have not been able to move that logic into the cbot.

Could you please point me in the right direction, feels like I am missing something obvious.

Best regards

Erik


@erik.lindblad

erik.lindblad
10 Jul 2020, 16:39

RE: RE:

erik.lindblad said:


Have the same issue. This is urgent! Please fix!

dave.mazza said:

The app crash when i tray to open!!!!

i haven’t another mode to access to the platform!!!

 

 

 

Works now!


@erik.lindblad

erik.lindblad
10 Jul 2020, 15:16

RE:


Have the same issue. This is urgent! Please fix!

dave.mazza said:

The app crash when i tray to open!!!!

i haven’t another mode to access to the platform!!!

 

 


@erik.lindblad

erik.lindblad
10 Jul 2020, 12:49

RE:

Same issue here. Problems started today. Reinstalled the app but crashes on opening.

/Erik

markyandrew said:

Hi, seems the support level is poor...your email doesn’t reply to Support queries apparently so this seems the only route....not great.

I’ve recently made the jump from MT4 app to your cTrader iOS app hoping for a better experience.....sadly so far I’m concerned with how the app performs.....it keeps crashing and then not reopening! That’s quite a fundamental problem, as a trader I need instant access and responsiveness.

I click the app icon the blue pepperstone screen for the broker app appears and then it closes before it’s even open. I force close the app and repeatedly try again but still won’t open. Randomly later I try and it might open only to repeat the above issues again later. I got a notification of a pending order being filled and wanted to check on it ASAP but I can’t....this is unacceptable and will prove costly to me as a trader.

Please help fix this issue and advise me....

Thanks 

 


@erik.lindblad

erik.lindblad
04 May 2020, 11:37

RE:

PanagiotisCharalampous said:

Hi erik.lindblad,

We have reproduced the issue and it will be fixed in an upcoming update. In the meanwhile a workaround is to change your region to one that considers "." as a separator through Settings -> General -> Region.

Best Regards,

Panagiotis 

Join us on Telegram

 

Sounds good. And thanks for the workaround!

/Erik


@erik.lindblad

erik.lindblad
04 May 2020, 00:23

RE:

PanagiotisCharalampous said:

Hi erik.lindblad,

We could not reproduce such a behavior. Can you please provide us the following as well?

1. iOS version

2. Device information

3. The symbol you are trying to trade

4. A screenshot of the application at the time you are putting the relevant value

Best Regards,

Panagiotis 

Join us on Telegram

 

Any news regarding this? Moreover, you cannot enter a take profit or stop loss price on an order in the iPhone app. You have to specify the take profit and stop loss in pips and you cannot edit the price level. I am sorry but this does not make sense to me.

Best Regards

Erik


@erik.lindblad

erik.lindblad
15 Apr 2020, 21:57 ( Updated at: 21 Dec 2023, 09:22 )

RE:

PanagiotisCharalampous said:

Hi erik.lindblad,

We could not reproduce such a behavior. Can you please provide us the following as well?

1. iOS version

2. Device information

3. The symbol you are trying to trade

4. A screenshot of the application at the time you are putting the relevant value

Best Regards,

Panagiotis 

Join us on Telegram

 

That is strange. I have an iPhone X

iOS version 13.3.1

It happens on all pairs when trying to edit a PENDING LIMIT order, I don't know about other order types, I only use limit orders.

When I try to modify the lot size using comma (there is no dot on the digital keyboard that appears) it clears the entire input field and gives an error message.


 


@erik.lindblad

erik.lindblad
15 Apr 2020, 15:59

RE:

PanagiotisCharalampous said:

Hi erik.lindblad,

This has been fixed in a recent update. Can you please tell us which version and broker do you use and which iPhone do you have?

Best Regards,

Panagiotis 

Join us on Telegram

Thanks for your reply!

I am using IC Markets and cTrader version 3.11.54240

/Erik


@erik.lindblad

erik.lindblad
24 May 2019, 16:30

RE:

Panagiotis Charalampous said:

Hi erik,

I don't see any obstacle in doing this. All the required infromation is there.

Best Regards,

Panagiotis

Thanks for your reply! I did not see your other reply until after my last post. Good news I can use a cBot for this! Yes, I get your point that price and timestamp are a bit contradicting. Maybe better to simulate a market order at the given timestamp.

Two more questions if you do not mind:

1. Can I mix symbols in the file or do I need to run once per symbol and then sum up the results?
2. Just to point me in the right direction, can I do this using the built-in backtesting features?

/Erik


@erik.lindblad

erik.lindblad
24 May 2019, 16:04

Hi and thanks for your reply!

Let's say I have 100 trades from January 2019 in a csv file. I want to know if these trades were wins or losses if they would have been placed at the given time/price.

File content could look somehting like this (date, time, symbol, type, price, sl and tp:

2019-01-05 05:32:456 EURUSD BUY, 1.1192 1.1170 1.1220

2019-01-06 22:32:123 CADJPY SELL, 84.20 84.50 83.50

etc ...

Could I get the outcome of these trades using backtesting functionality in cTrader/cAlgo?

Hope that was clear.

/Erik


@erik.lindblad

erik.lindblad
08 Apr 2019, 19:00

This is issue is now resolved. It had to do with multiple threads interfering with each other and I have found a way to solve that.


@erik.lindblad

erik.lindblad
18 Mar 2019, 17:09

RE:

Panagiotis Charalampous said:

Hi Erik,

If you can create a cBot that reproduces the exception without using all these components then it will be easier for us to assist you. In order for us to help you, we need to be able to reproduce the problem.

Best Regards,

Panagiotis

I understand, I will try to make a small stand alone bot and see if I can reproduce the error with that. I'll report back here in a few days.

Cheers
Erik


@erik.lindblad

erik.lindblad
18 Mar 2019, 16:09

RE:

Panagiotis Charalampous said:

Hi Erik,

Since there are may components and references in this cBot, please send me the compressed solution folder at community@spotware.com.

Best Regards,

Panagiotis 

Thanks for your reply. I don't have any problem with sending you the code or the assemblies. But the code is a seperate client application which is integrated with Telegram. The application listens to a Telegram channel and collects signals containing trade setups that are parsed and sent to the cBot for execution. You need a Telegram account and you will need to post trade signals on a specific format in a specific channel to create any interaction with the bot. You need credentials to connect to Telegram and they will be sent to my phone number when you start the application etc. I think you will get a hard time running it.

Correct me if I am wrong, but I can't see how the client calling the cBots methods are relevant here? The bot is very simple, it has only three methods. One to get market data for a symbol, one to give me the account balance and one to execute trades.

Still want me to send you code and/or assemblies? You do not have access to any error logs do you? Where you can track down the errors based on my account or the positionIds or something?

Cheers Erik


@erik.lindblad

erik.lindblad
18 Mar 2019, 01:42 ( Updated at: 21 Dec 2023, 09:21 )

RE:

Panagiotis Charalampous said:

Hi Erik,

Indeed it is a lot of code. Did you check yourself if all the parameters passed to the ExecuteMarketOrder are not null?

Best Regards,

Panagiotis

Hello again Panagiotis! I have now spent a week on refactoring my code making my bot class minimal. So forget about all previous code posted. But the problem remains. Around one in three market orders still crashes in OnPositionOpened. I do not pass anything that is null. I'm now even logging my paramters just before calling ExecuteMarketOrder. Could you please have a look at my bot class.

It's just trade type, symbol, volume, stop loss pips, takeprofit pips and comment which is just a small json string. How could this cause a nullreference? Please help me, it is absolutely key for the bot to place a market order and I have put so much time and effort into this. 

Really appreciate your help.

Best regards

Erik

 


namespace MasterlessTrading
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using cAlgo.API;
    using MasterlessTrading.Core;
    using MasterlessTrading.Ipc;
    using MasterlessTrading.Ipc.Contracts;
    using Newtonsoft.Json;

    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class RoninBot : Robot
    {
        [Parameter(DefaultValue = 9712)]
        public int ServerPort { get; set; }

        private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();

        private IpcServer _server;
        public const string ProviderGreen = "Green";
        public const string ProviderRonin = "Ronin";

        protected override void OnStart()
        {
            Loggers.Register(Print, new[] 
            {
                LoggingLevel.Debug,
                LoggingLevel.Info,
                LoggingLevel.Warn,
                LoggingLevel.Error
            });

            _server = IpcServer.Start(Guid.Empty, this.ServerPort, new[] 
            {
                typeof(RoninSignal).Assembly
            });

            _server.Consume<AccountDataRequest, AccountDataResponse>((request, context) => { return new AccountDataResponse {Balance = Account.Balance}; });

            _server.Consume<MarketDataRequest, MarketDataResponse>((request, context) =>
            {
                var symbol = MarketData.GetSymbol(request.Symbol);

                return new MarketDataResponse 
                {
                    AskPrice = symbol.Ask,
                    BidPrice = symbol.Bid,
                    PipSize = symbol.PipSize,
                    PipValue = symbol.PipValue
                };
            });


            _server.Consume<OrderExecutionRequest, OrderExecutionReponse>((request, context) =>
            {
                ExecuteOrders(request.Orders);
                return new OrderExecutionReponse();
            });

            _server.Consume<PositionCloseRequest, PositionCloseResponse>((request, context) =>
            {
                ClosePosition(request.ProviderMessageId);
                return new PositionCloseResponse();
            });

            _server.Consume<TestRequest, TestResponse>((request, context) => { return new TestResponse {Count = DateTime.Now.Millisecond}; });

            // Subscribe to events
            Positions.Closed += PositionsOnClosed;
        }
        protected override void OnStop()
        {
            _server.Dispose();
            _cancellationTokenSource.Cancel();
        }

        private void ExecuteOrders(List<Order> orders)
        {
            if (orders == null)
            {
                return;
            }

            foreach (var order in orders)
            {
                var symbol = MarketData.GetSymbol(order.Symbol);
                var volume = symbol.NormalizeVolumeInUnits(order.Volume);
                var takeProfitPips = Math.Abs(Math.Round((order.EntryPrice.Value - order.TakeProfitPrice) / symbol.PipSize));
                var stopLossPips = Math.Abs(Math.Round((order.EntryPrice.Value - order.StopLossPrice) / symbol.PipSize));

                TradeResult result = null;

                var metadata = JsonConvert.SerializeObject(new OrderMetaData 
                {
                    Provider = order.Provider,
                    ProviderMessageId = order.ProviderMessageId
                });

                switch (order.Type)
                {
                    case OrderType.BuyLimit:
                        result = PlaceLimitOrder(TradeType.Buy, symbol, volume, order.EntryPrice.Value, "", stopLossPips, takeProfitPips, null, metadata);
                        break;
                    case OrderType.SellLimit:
                        result = PlaceLimitOrder(TradeType.Sell, symbol, volume, order.EntryPrice.Value, "", stopLossPips, takeProfitPips, null, metadata);
                        break;
                    case OrderType.BuyStop:
                        result = PlaceStopOrder(TradeType.Buy, symbol, volume, order.EntryPrice.Value, "", stopLossPips, takeProfitPips, null, metadata);
                        break;
                    case OrderType.SellStop:
                        result = PlaceStopOrder(TradeType.Sell, symbol, volume, order.EntryPrice.Value, "", stopLossPips, takeProfitPips, null, metadata);
                        break;
                    case OrderType.BuyMarket:
                        Print("Printing order parameters: Type: " + order.Type + " Symbol: " + symbol + " Volume: " + volume + " StopLossPips: " + stopLossPips + " TakeProfitPips: " + takeProfitPips);
                        result = ExecuteMarketOrder(TradeType.Buy, symbol, volume, "", stopLossPips, takeProfitPips);
                        break;
                    case OrderType.SellMarket:
                        Print("Printing order parameters: Type: " + order.Type + " Symbol: " + symbol + " Volume: " + volume + " StopLossPips: " + stopLossPips + " TakeProfitPips: " + takeProfitPips);
                        result = ExecuteMarketOrder(TradeType.Sell, symbol, volume, "", stopLossPips, takeProfitPips);
                        break;
                    default:
                        break;
                }

                if (result.IsSuccessful)
                {
                    if (result.Position != null)
                    {
                        try
                        {
                            result.Position.ModifyStopLossPrice(order.StopLossPrice);
                            result.Position.ModifyTakeProfitPrice(order.TakeProfitPrice);
                            Print("Stop loss and take profit modified on market order.");
                        }
                        catch (Exception e)
                        {
                            Print("Failed to modify SL or TP: " + e.Message + " " + e.StackTrace);
                        }
                    }
                }
            }
        }

        private void PositionsOnClosed(PositionClosedEventArgs args)
        {
            if (args.Reason == PositionCloseReason.TakeProfit)
            {
                var metaData = JsonConvert.DeserializeObject<OrderMetaData>(args.Position.Comment);
                switch (metaData.Provider)
                {
                    case ProviderRonin:
                        MoveConnectedStopLossesToBreakEven(metaData.ProviderMessageId);
                        break;
                    case ProviderGreen:
                        return;
                }
            }
        }

        private void MoveConnectedStopLossesToBreakEven(int providerMessageId)
        {
            var connectedPositions = Positions.Where(p => JsonConvert.DeserializeObject<OrderMetaData>(p.Comment).ProviderMessageId == providerMessageId);
            if (connectedPositions != null && connectedPositions.Any())
            {
                foreach (var connectedPosition in connectedPositions)
                {
                    var result = connectedPosition.ModifyStopLossPrice(connectedPosition.EntryPrice);
                    if (result.IsSuccessful)
                        Print("Stop loss moved to break even. Label:" + connectedPosition.Label);
                    else
                        Print("Failed to move stop loss moved to break even. Label:" + connectedPosition.Label);
                }
            }
        }

        private void ClosePosition(int providerMessageId)
        {
            var position = Positions.FirstOrDefault(p => JsonConvert.DeserializeObject<OrderMetaData>(p.Comment).ProviderMessageId == providerMessageId);
            if (position != null)
            {
                var result = position.Close();
                if (result.IsSuccessful)
                    Print("Position closed: " + position.Label);
                else
                    Print("Failed to close position: " + position.Label);
            }
            else
            {
                Print(string.Format("Failed to close position. Could not find position with ProviderMessageId: {0}", providerMessageId));
            }
        }
    }
}

 


@erik.lindblad

erik.lindblad
06 Mar 2019, 14:27

No unfortunately not. Since I have had a hard time debugging the bot I have so far relied on logging, both the Print method and the built-in logging. If you look at the log lines in my first post it looks like everything I have passed as an argument had a value. But I am doing some refactoring now and trying to do the bot more light weight by moving business logic outside the bot. Then troubleshooting shuld be easier. I will get back here and report when I am done.

/Erik


@erik.lindblad

erik.lindblad
05 Mar 2019, 18:42

@Panagiotis Guess it could be a bit ovewhelming to look at all that code. But if we focus on the execution part. The market order is successfully placed on the market so what can be wrong with the arguments I pass to the ExecuteMarketOrder method? The error occurrs after the order is placed, leading me to my next question - what could possibly cause a NullReferenceException in Postions.Opened, when I am not even using that event? 


@erik.lindblad

erik.lindblad
05 Mar 2019, 16:27

Sure, this is all code used by the bot excluding the code communicating with the signal provider application, which should not affect the order execution I guess. The bot recevies signals from different signal providers, validates the signals, makes a sanity check on the provided take profits and stop losses, and then places the order. I guess the order execution part is the key part here. After the trade is placed I do not act on any specific events except Positions.Closed, so I cannot really see what is happening inside Postions.Opened which I do not use.
 

namespace MasterlessTrading
{
    using System;

    public class Order
    {
        public string Label { get; set; }
        public string Symbol { get; set; }
        public double Volume { get; set; }
        public double StopLossPrice { get; set; }
        public double TakeProfitPrice { get; set; }
        public OrderType Type { get; set; }
        public double? EntryPrice { get; set; }
        public DateTime TimeStamp { get; set; }
        public string MetaData { get; set; }
    }
}
using MasterlessTrading.Telegram;
using MasterlessTrading.Telegram.Core;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MasterlessTrading
{
    using MasterlessTrading.Ipc.Contracts;

    public class OrderFactory
    {
        private const string ProviderGreen = "Green";
        private const string ProviderRonin = "Ronin";

        public List<Order> CreateRoninOrders(RoninSignal signal, MarketData marketData, TradeSettings tradeSettings, double accountBalance, string provider)
        {
            var orders = signal
                .TakeProfits
                .Select(tp => new Order
                {
                    TimeStamp = signal.Date,
                    Symbol = signal.Currency.ToString(),
                    Type = GetOrderType(signal.TradeType),
                    EntryPrice = signal.EntryPrice,
                    TakeProfitPrice = tp.Price,
                    StopLossPrice = signal.StopLossPrice,
                    Label = string.Format("{0:yyyy-MM-dd HH:mm:ss} {1} {2} {3} Provider: {4} MessageId: {5}", signal.Date, signal.Currency.ToString(), signal.TradeType, signal.EntryPrice, ProviderRonin, signal.ProviderMessageId),
                    MetaData = JsonConvert.SerializeObject(new OrderMetaData()
                    {
                        Provider = ProviderRonin,
                        ProviderMessageId = signal.ProviderMessageId
                    })
                }).ToList();

            SetPositionSizes(orders, marketData, tradeSettings, accountBalance, provider);

            return orders;
        }
        public List<Order> CreateGreenOrders(GreenSignal signal, MarketData marketData, TradeSettings tradeSettings, double accountBalance, string provider)
        {
            var orders = new List<Order>();
            var order = new Order
            {
                TimeStamp = signal.Date,
                Symbol = signal.Currency.ToString(),
                Type = GetOrderType(signal.TradeType),
                EntryPrice = signal.EntryPrice,
                TakeProfitPrice = signal.TakeProfit,
                StopLossPrice = signal.StopLoss,
                Label = string.Format("{0:yyyy-MM-dd HH:mm:ss} {1} {2} {3} Provider: {4} MessageId: {5}", signal.Date, signal.Currency, signal.TradeType, signal.EntryPrice, ProviderGreen, signal.ProviderMessageId),
                MetaData = JsonConvert.SerializeObject(new OrderMetaData()
                {
                    Provider = ProviderGreen,
                    ProviderMessageId = signal.ProviderMessageId
                })
            };
            orders.Add(order);
            SetPositionSizes(orders, marketData, tradeSettings, accountBalance, provider);
            return orders;
        }

        private void SetPositionSizes(List<Order> orders, MarketData marketData, TradeSettings tradeSettings, double accountBalance, string provider)
        {
            var dollarRiskPerTrade = accountBalance * tradeSettings.GetPercentageRiskPerTrade(provider) / 100;

            foreach (var order in orders)
            {
                var stopLossPips = Math.Abs(Math.Round((order.EntryPrice.Value - order.StopLossPrice) / marketData.PipSize));
                var volume = dollarRiskPerTrade / (stopLossPips * marketData.PipValue);
                order.Volume = volume;
            }
        }

        private OrderType GetOrderType(TradeType type)
        {
            switch (type)
            {
                case TradeType.Buy:
                    return OrderType.BUY;
                case TradeType.BuyStop:
                    return OrderType.BUYSTOP;
                case TradeType.Sell:
                    return OrderType.SELL;
                case TradeType.SellStop:
                    return OrderType.SELLSTOP;
                case TradeType.BuyLimit:
                    return OrderType.BUYLIMIT;
                case TradeType.SellLimit:
                    return OrderType.SELLLIMIT;
                default:
                    throw new ArgumentOutOfRangeException("type", type, null);
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MasterlessTrading
{
    public class OrderMetaData
    {
        public string Provider { get; set; }
        public int ProviderMessageId { get; set; }
    }
}
namespace MasterlessTrading
{
    public enum OrderType
    {
        BUY,
        SELL,
        BUYLIMIT,
        SELLLIMIT,
        BUYSTOP,
        SELLSTOP,
        BUYMARKET,
        SELLMARKET,
        UNKNOWN
    }
}

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MasterlessTrading
{
    public class OrderValidator
    {
        public ValidationResult ValidateOrder(Order order, MarketData marketData, TradeSettings tradeSettings)
        {
            if (order.EntryPrice == 0 || order.StopLossPrice == 0 || order.TakeProfitPrice == 0)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid order. Entry, SL and TP must be provided"
                };
            }

            AdjustOrderType(order, marketData, tradeSettings);


            switch (order.Type)
            {
                case OrderType.BUYLIMIT:
                    return ValidateBuyLimitOrder(order, marketData.AskPrice);
                case OrderType.BUYSTOP:
                    return ValidateBuyStopOrder(order, marketData.AskPrice);
                case OrderType.BUYMARKET:
                    return ValidateBuyMarketOrder(order, marketData.AskPrice);
                case OrderType.SELLLIMIT:
                    return ValidateSellLimitOrder(order, marketData.BidPrice);
                case OrderType.SELLSTOP:
                    return ValidateSellStopOrder(order, marketData.BidPrice);
                case OrderType.SELLMARKET:
                    return ValidateSellMarketOrder(order, marketData.BidPrice);
                default:
                    return new ValidationResult()
                    {
                        IsValid = false,
                        ErrorMessage = "Invalid order. Unknown order type."
                    };
            }
        }

        private void AdjustOrderType(Order order, MarketData marketData, TradeSettings tradeSettings)
        {
            if (order.Type == OrderType.SELL)
            {
                // Change to stop or limit order depedning on market price
                if (order.EntryPrice.Value > marketData.BidPrice)
                    order.Type = OrderType.SELLLIMIT;
                else
                    order.Type = OrderType.SELLSTOP;
            }
            if (order.Type == OrderType.BUY)
            {
                // Change to stop or limit order depedning on market price
                if (order.EntryPrice.Value < marketData.AskPrice)
                    order.Type = OrderType.BUYLIMIT;
                else
                    order.Type = OrderType.BUYSTOP;
            }

            if (order.Type == OrderType.SELLSTOP || order.Type == OrderType.SELLLIMIT)
            {
                // Change to market order if entry price is not below market price
                var priceOffset = Math.Abs((marketData.BidPrice - order.EntryPrice.Value) / marketData.PipSize);
                if (priceOffset <= tradeSettings.MaximumPipOffset)
                    order.Type = OrderType.SELLMARKET;
            }
            if (order.Type == OrderType.BUYSTOP || order.Type == OrderType.BUYLIMIT)
            {
                // Change to market order if entry price is not below market price
                var priceOffset = Math.Abs((marketData.AskPrice - order.EntryPrice.Value) / marketData.PipSize);
                if (priceOffset <= tradeSettings.MaximumPipOffset)
                    order.Type = OrderType.BUYMARKET;
            }
        }

        private ValidationResult ValidateSellMarketOrder(Order order, double currentBidPrice)
        {
            if (order.StopLossPrice <= currentBidPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid sell market order. SL must be above market price."
                };
            }

            if (order.TakeProfitPrice >= currentBidPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid sell market order. TP must be below market price."
                };
            }

            return new ValidationResult() { IsValid = true };
        }
        private ValidationResult ValidateBuyMarketOrder(Order order, double currentAskPrice)
        {
            if (order.StopLossPrice >= currentAskPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid buy market order. SL must be below entry."
                };
            }

            if (order.TakeProfitPrice <= currentAskPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid buy market order. TP must be above entry."
                };
            }

            return new ValidationResult() { IsValid = true };
        }

        private ValidationResult ValidateSellStopOrder(Order order, double currentBidPrice)
        {
            if (order.StopLossPrice <= order.EntryPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid sell stop order. SL must be above entry."
                };
            }

            if (order.TakeProfitPrice >= order.EntryPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid sell stop order. TP must be below entry."
                };
            }

            if (order.EntryPrice >= currentBidPrice)
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid sell stop order. Entry must be below market price."
                };

            return new ValidationResult() { IsValid = true };
        }

        private ValidationResult ValidateBuyStopOrder(Order order, double currentAskPrice)
        {
            if (order.StopLossPrice >= order.EntryPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid buy stop order. SL must be below entry."
                };
            }

            if (order.TakeProfitPrice <= order.EntryPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid buy stop order. TP must be above entry."
                };
            }

            if (order.EntryPrice <= currentAskPrice)
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid buy stop order. Entry must be above market price."
                };

            return new ValidationResult() { IsValid = true };
        }

        private ValidationResult ValidateSellLimitOrder(Order order, double currentBidPrice)
        {
            if (order.StopLossPrice <= order.EntryPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid sell limit order. SL must be above entry."
                };
            }

            if (order.TakeProfitPrice >= order.EntryPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid sell limit order. TP must be below entry."
                };
            }

            if (order.EntryPrice <= currentBidPrice)
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid sell limit order. Entry must be above market price."
                };

            return new ValidationResult() { IsValid = true };
        }

        private ValidationResult ValidateBuyLimitOrder(Order order, double currentAskPrice)
        {
            if (order.StopLossPrice >= order.EntryPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid buy limit order. SL must be below entry."
                };
            }

            if (order.TakeProfitPrice <= order.EntryPrice)
            {
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid buy limit order. TP must be above entry."
                };
            }

            if (order.EntryPrice >= currentAskPrice)
                return new ValidationResult()
                {
                    IsValid = false,
                    ErrorMessage = "Invalid buy limit order. Entry must be below market price."
                };

            return new ValidationResult() { IsValid = true };
        }

        public class ValidationResult
        {
            public bool IsValid { get; set; }
            public string ErrorMessage { get; set; }
        }
    }
}

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MasterlessTrading.Telegram;
using MasterlessTrading.Telegram.Core;

namespace MasterlessTrading
{
    using cAlgo.API;
    using MasterlessTrading.Ipc;
    using MasterlessTrading.Ipc.Contracts;
    using TradeType = MasterlessTrading.Ipc.Contracts.TradeType;

    public class RoninSignalTransformer
    {
        public void TransformSignal(RoninSignal signal, MarketData marketData)
        {
            AdjustStopLoss(signal, marketData);
            AdjustTakeProfits(signal, marketData);
            RoundPrices(signal);
        }

        private void AdjustStopLoss(RoninSignal signal, MarketData marketData, double minimumStopLoss = 30, double maximumStopLoss = 50, double defaultStopLoss = 40)
        {
            var stopLossPips = Math.Round((signal.EntryPrice - signal.StopLossPrice) / marketData.PipSize);
            if (Math.Abs(stopLossPips) < minimumStopLoss || Math.Abs(stopLossPips) > maximumStopLoss)
            {
                if (signal.TradeType == TradeType.Buy || signal.TradeType == TradeType.BuyStop || signal.TradeType == TradeType.BuyLimit)
                    signal.StopLossPrice = signal.EntryPrice - (defaultStopLoss * marketData.PipSize);
                else if (signal.TradeType == TradeType.Sell || signal.TradeType == TradeType.SellStop || signal.TradeType == TradeType.SellLimit)
                    signal.StopLossPrice = signal.EntryPrice + (defaultStopLoss * marketData.PipSize);
            }
        }

        private void AdjustTakeProfits(RoninSignal signal, MarketData marketData)
        {
            var takeProfitPips = new int[3] { 20, 40, 100 };
            var index = 0;
            foreach (var tp in signal.TakeProfits)
            {
                var tpPips = takeProfitPips[index++];
                tp.Pips = tpPips;
                if (signal.TradeType == TradeType.Buy || signal.TradeType == TradeType.BuyStop || signal.TradeType == TradeType.BuyLimit)
                    tp.Price = signal.EntryPrice + (tpPips * marketData.PipSize);
                else if (signal.TradeType == TradeType.Sell || signal.TradeType == TradeType.SellStop || signal.TradeType == TradeType.SellLimit)
                    tp.Price = signal.EntryPrice - (tpPips * marketData.PipSize);
            }
        }

        private void RoundPrices(RoninSignal signal)
        {
            var noOfdecimals = signal.Currency.ToString().Contains("JPY") ? 3 : 4;
            signal.EntryPrice = Math.Round(signal.EntryPrice, noOfdecimals);
            signal.StopLossPrice = Math.Round(signal.StopLossPrice, noOfdecimals);
            foreach (var tp in signal.TakeProfits)
            {
                tp.Price = Math.Round(tp.Price, noOfdecimals);
            }
        }
    }
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MasterlessTrading
{
    public class TradeSettings
    {
        private double _percentageRiskPerTrade;
        public const string ProviderGreen = "Green";
        public const string ProviderRonin = "Ronin";

        public TradeSettings(double percentageRiskPerTrade, double maximumPipOffset)
        {
            MaximumPipOffset = maximumPipOffset;
            _percentageRiskPerTrade = percentageRiskPerTrade;
        }

        public double GetPercentageRiskPerTrade(string provider)
        {
            return provider == ProviderRonin ? _percentageRiskPerTrade / 3 : _percentageRiskPerTrade;
        }

        public double MaximumPipOffset { get; private set; }
    }
}

 


@erik.lindblad

erik.lindblad
05 Mar 2019, 16:05

RE:

erisoftdevelop said:

The problem is in the Position.Opened event so you have some code affecting inside the event and take a look there.

Hi and thanks for your reply! What you are saying makes sense but I do not use that event, I have not subscribed to it at all. That's what's confusing me.

Or am I missing something?

/Erik


@erik.lindblad

erik.lindblad
05 Mar 2019, 16:02

Hi Panagiotis! Thanks for your reply. Sure, I can post the code, although it is almost 200 lines. I am using the cBot as a trade executor only. Receving signals from another application I have built. So the cBot basically just takes a trade signal, validates it and places an order of the correct type on the market. So the bot handles all symbols and do not care abut time frames etc, it just executes trades. Only the market order fails, and not all of them, but maybe 30%. Here is the code.
 


namespace MasterlessTrading
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using cAlgo.API;
    using MasterlessTrading.Ipc;
    using MasterlessTrading.Ipc.Contracts;
    using Newtonsoft.Json;

    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class RoninBot : Robot
    {
        [Parameter(DefaultValue = 9712)]
        public int ServerPort { get; set; }

        [Parameter(DefaultValue = 2.0)]
        public double PercentageRiskPerTrade { get; set; }

        [Parameter(DefaultValue = 1.0)]
        public double MaximumPipOffset { get; set; }

        private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();

        private TradeSettings _tradeSettings;
        private OrderFactory _orderFactory;
        private OrderValidator _orderValidator;
        private RoninSignalTransformer _roninSignalValidator;
        private IpcServer _server;
        public const string ProviderGreen = "Green";
        public const string ProviderRonin = "Ronin";

        protected override void OnStart()
        {
            _orderFactory = new OrderFactory();
            _orderValidator = new OrderValidator();
            _roninSignalValidator = new RoninSignalTransformer();
            _tradeSettings = new TradeSettings(PercentageRiskPerTrade, MaximumPipOffset);

            var tgClient = Guid.Parse("5C0A5345-2798-47A8-BC41-6598C41E2051");

            _server = IpcServer.Listen(Guid.Empty, this.ServerPort, new[]
            {
                typeof(RoninSignal).Assembly
            }, Print);

            _server.Consume<RoninSignal>(signal =>
            {
                DebugLog(tgClient, signal);

                var marketData = GetMarketData(signal.Currency.ToString());
                _roninSignalValidator.TransformSignal(signal, marketData);
                var orders = _orderFactory.CreateRoninOrders(signal, marketData, _tradeSettings, Account.Balance, ProviderRonin);
                var validOrders = GetValidOrders(orders, marketData);
                ExecuteOrders(validOrders);
            });

            _server.Consume<GreenSignal>(signal =>
            {
                DebugLog(tgClient, signal);

                var marketData = GetMarketData(signal.Currency.ToString());
                var orders = _orderFactory.CreateGreenOrders(signal, marketData, _tradeSettings, Account.Balance, ProviderGreen);
                var validOrders = GetValidOrders(orders, marketData);
                ExecuteOrders(validOrders);
            });

            _server.Consume<GreenInstruction>(signal =>
            {
                DebugLog(tgClient, signal);

                switch (signal.InstructionType)
                {
                    case InstructionType.Hold:
                        // keep going
                        break;
                    case InstructionType.Close:
                        ClosePosition(signal.SubjectMessageId);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            });

            // Subscribe to events
            Positions.Closed += PositionsOnClosed;

        }

        private void DebugLog(Guid tgClient, object signal)
        {
            _server.Send(tgClient, new Message
            {
                Headers = new MessageHeaders
                {
                    MessageId = Guid.NewGuid(),
                    CorrelationId = Guid.NewGuid()
                },
                Payload = new LogMessage
                {
                    Text = "Received: " + JsonConvert.SerializeObject(signal)
                }
            });
        }

        protected override void OnTick()
        {
        }

        protected override void OnStop()
        {
            _server.Dispose();
            _cancellationTokenSource.Cancel();
        }

        private MarketData GetMarketData(string symbol)
        {
            var symbolData = MarketData.GetSymbol(symbol);
            return new MarketData
            {
                AskPrice = symbolData.Ask,
                BidPrice = symbolData.Bid,
                PipSize = symbolData.PipSize,
                PipValue = symbolData.PipValue
            };
        }

        private List<Order> GetValidOrders(List<Order> orders, MarketData marketData)
        {
            var validOrders = new List<Order>();
            foreach (var order in orders)
            {
                var validationResult = _orderValidator.ValidateOrder(order, marketData, _tradeSettings);
                if (validationResult.IsValid)
                    validOrders.Add(order);
                else
                    Print(validationResult.ErrorMessage);
            }
            return validOrders;
        }

        private void ExecuteOrders(List<Order> orders)
        {
            foreach (var order in orders)
            {
                var symbol = MarketData.GetSymbol(order.Symbol.ToString());
                var takeProfitPips = Math.Abs(Math.Round((order.EntryPrice.Value - order.TakeProfitPrice) / symbol.PipSize));
                var stopLossPips = Math.Abs(Math.Round((order.EntryPrice.Value - order.StopLossPrice) / symbol.PipSize));

                order.Volume = symbol.NormalizeVolumeInUnits(order.Volume);

                TradeResult result = null;

                switch (order.Type)
                {
                    case OrderType.BUYLIMIT:
                        result = PlaceLimitOrder(cAlgo.API.TradeType.Buy, symbol, order.Volume, order.EntryPrice.Value, order.Label, stopLossPips, takeProfitPips, null, order.MetaData);
                        break;
                    case OrderType.SELLLIMIT:
                        result = PlaceLimitOrder(cAlgo.API.TradeType.Sell, symbol, order.Volume, order.EntryPrice.Value, order.Label, stopLossPips, takeProfitPips, null, order.MetaData);
                        break;
                    case OrderType.BUYSTOP:
                        result = PlaceStopOrder(cAlgo.API.TradeType.Buy, symbol, order.Volume, order.EntryPrice.Value, order.Label, stopLossPips, takeProfitPips, null, order.MetaData);
                        break;
                    case OrderType.SELLSTOP:
                        result = PlaceStopOrder(cAlgo.API.TradeType.Sell, symbol, order.Volume, order.EntryPrice.Value, order.Label, stopLossPips, takeProfitPips, null, order.MetaData);
                        break;
                    case OrderType.BUYMARKET:
                        result = ExecuteMarketOrder(cAlgo.API.TradeType.Buy, symbol, order.Volume, order.Label, stopLossPips, takeProfitPips, null, order.MetaData);
                        break;
                    case OrderType.SELLMARKET:
                        result = ExecuteMarketOrder(cAlgo.API.TradeType.Sell, symbol, order.Volume, order.Label, stopLossPips, takeProfitPips, null, order.MetaData);
                        break;
                    default:
                        LogOrder(order, "Order type not recognized.", LogMessageType.Warning);
                        break;
                }
            }
        }

        private void PositionsOnClosed(PositionClosedEventArgs args)
        {
            if (args.Reason == PositionCloseReason.TakeProfit)
            {
                var metaData = JsonConvert.DeserializeObject<OrderMetaData>(args.Position.Comment);
                switch (metaData.Provider)
                {
                    case ProviderRonin:
                        MoveConnectedStopLossesToBreakEven(metaData.ProviderMessageId);
                        break;
                    case ProviderGreen:
                        return;
                }
            }
        }

        private void MoveConnectedStopLossesToBreakEven(int providerMessageId)
        {
            var connectedPositions = Positions.Where(p => JsonConvert.DeserializeObject<OrderMetaData>(p.Comment).ProviderMessageId == providerMessageId);
            if (connectedPositions != null && connectedPositions.Any())
            {
                foreach (var connectedPosition in connectedPositions)
                {
                    connectedPosition.ModifyStopLossPrice(connectedPosition.EntryPrice);
                    Print("Stop loss moved to break even. Label:" + connectedPosition.Label);
                }
            }
        }

        private void ClosePosition(int providerMessageId)
        {
            var position = Positions.FirstOrDefault(p => JsonConvert.DeserializeObject<OrderMetaData>(p.Comment).ProviderMessageId == providerMessageId);
            if (position != null)
            {
                position.Close();
                Print("Position closed: " + position.Label);
            }
            else
            {
                Print(string.Format("Failed to close position. Could not find position with ProviderMessageId: {0}", providerMessageId));
            }
        }

        private void LogOrder(Order order, string message, LogMessageType messageType)
        {
            Print(string.Format("{0}: {1} Label: {2} SL: {3} TP: {4} metadata: {5}", messageType.ToString(), message, order.Label, order.StopLossPrice, order.TakeProfitPrice, order.MetaData));
        }

        private void LogExecutionResult(TradeResult result, string message, LogMessageType messageType)
        {
            if (!result.IsSuccessful)
            {
                Print(string.Format("{0}: {1} {2}", messageType.ToString(), message, result.Error));
            }

            if (result.Position != null)
            {
                Print(string.Format("{0}: {1} {2} {3} {4} Entry price: {5} SL: {6} TP: {7}", messageType.ToString(), message, result.Position.EntryTime.ToString("yyyy-MM-dd HH:mm:ss"), result.Position.TradeType, result.Position.SymbolCode, result.Position.EntryPrice, result.Position.StopLoss, result.Position.TakeProfit));
            }
            else if (result.PendingOrder != null)
            {
                Print(string.Format("{0}: {1} {2} {3} {4} Entry price: {5} SL: {6} TP: {7}", messageType.ToString(), message, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), result.PendingOrder.TradeType, result.PendingOrder.SymbolCode, result.PendingOrder.TargetPrice, result.PendingOrder.StopLoss, result.PendingOrder.TakeProfit));
            }
        }
    }
}

 


@erik.lindblad