Replies

ketos.energy
29 Dec 2020, 09:12

RE:

PanagiotisCharalampous said:

Hi ketos.energy,

There is no specific intention behind this except the fact that it takes less space and looks cleaner. It is just that AFAIK nobody ever requested this information. If you think it is important, post a suggestion and if it gathers enough votes, we will consider it.

Best Regards,

Panagiotis 

Join us on Telegram

Thank you so much for your super fast answers :) For me, it was just very confusing and this gave my the input to think that in backtesting, there is only bar-data and not tick-data used. This was the reason in the first place I asked. When I do backtesting, I'd like to have the closest results to the real world. And if I compare the times, I see a difference. So in my world, I think that there is a difference. If data is here, I'd prefer having the closes look as possible. But this is just my opinion. Just confusing like this :)

 

Enjoy the holidays.


@ketos.energy

ketos.energy
29 Dec 2020, 08:46

RE:

PanagiotisCharalampous said:

Hi linkersx,

Why not? I've seen simulation of all kind of latencies in back testing. Of course these algorithms really run on 100% tick Back test!

Because it is not possible to know how the network and LP would have behaved in real time in past date e.g. at a point in time in 2017. If you are referring to some random addition of latency in the trading process, then this is something different and it is definitely not realistic and not accurate.

 Best Regards,

Panagiotis 

Join us on Telegram

I completey understood that it is not possible to know the latency etc. The thing I don't understand is: The real tick will not be at the exact minute with second and millisecond to 0. Since you have the exact tick at let's say 10:04:03.345, why do you not show that but instead you round to the minute in backtesting as you mentionned before ("Backtesting is rounding the entry time to the minute, if this is what is confusing you. ") . So this means backtesting whould show 10:04 instead of 10:04:03.345.

Why? Why not showing the exact time if you have it?

This is what I don't understand @Panagiotis


@ketos.energy

ketos.energy
28 Dec 2020, 23:15

RE:

PanagiotisCharalampous said:

Hi ketos.energy,

Backtesting is rounding the entry time to the minute, if this is what is confusing you. 

Best Regards,

Panagiotis 

Join us on Telegram

Hi Panagiotis

Thank you for your reply. However, I don't understand: If you have the correct data to the tick, why would you round to the minute? So the data for every tick is there but you adapt the GUI to the minute? Don't understand why you would do that, if the data is there anyway... Could you explain?


@ketos.energy

ketos.energy
22 Dec 2020, 16:05

RE:

PanagiotisCharalampous said:

Also I cannot see where do you see a minute difference. Can you please point me to the deal you referring to? 

Hello dear Panagiotis

 

Please find the code attached here.

The time-frame I tested was: 2020/12/04-2020/12/17

Trading-Pair: GBPUSD

Params:
Volume: 10000
Buy-Start-Pips: 1
Buy TP Pips: 60
Buy SL Pips: 15
Sell-Start-Pips: 1
Sell TP Pips: 60
Sell SL Pips: 15
Spread-Toleranz: 0.5
Start UTC: 6
End-Stunde UTC: 21
End-Minute UTC: 54
Logs: Yes

 

Code:

using System;
using System.Linq;
using System.Collections.Generic;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;

/*
Kurzbeschreibung der Logik:
- Es werden je 1 Buy und 1 Sell Order automatisch gesetzt. Pro Order können die Parameter XStartPips TakeProfit-Pips und StopLoss-Pips gesetzt werden..
- Bei einem TP passiert keine weitere Logik
- Bei einem Loss einer Position wird erneut derselbe Order eröffnet.
- Beim Stoppen des Bots werden nur alle Order gestoppt. Die offenen Positionen bleiben offen.
  Der Bot kann danach neu gestartet werden und es werden nur die Orders eröffnet, wo nicht bereits eine Position offen ist.

Feinheiten:
- Der Bot ist nur zur Running-Time aktiv. Parametrisierbar sind Strat- und Endzeit.
- Zur Startzeit werden Buy- und Sell-Order automatisch gesetzt entsprechend mit xStartPips Differenz zum aktullen Ask / Bid-Preis.
- Nach Ende der Running-Time werden alle Orders und Positionen automatisch geschlossen und der Bot wartet erneut bis zur Start-Zeit.
- Zusätzlich kontrollieren wir beim Wiedereröffnen nach einem SL den aktuellen Spread. Ist er grösser als das SL, wird kein Order eröffnet.
  Dies sollte durch die Pause oben (fast) nie passieren. Mit der Logik haben wir aber einen Backup-Plan.
*/
namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ExcellenceOld : Robot
    {
        // ###### PARAMS ######
        [Parameter(DefaultValue = 10000)]
        public double Volume { get; set; }
        [Parameter("Buy Start-Pips", DefaultValue = 2)]
        public int BuyStartPips { get; set; }
        [Parameter("Buy TP Pips", DefaultValue = 250)]
        public double BuyTpPips { get; set; }
        [Parameter("Buy SL Pips", DefaultValue = 3)]
        public double BuySlPips { get; set; }
        [Parameter("Sell Start-Pips", DefaultValue = 2)]
        public int SellStartPips { get; set; }
        [Parameter("Sell TP Pips", DefaultValue = 250)]
        public double SellTpPips { get; set; }
        [Parameter("Sell SL Pips", DefaultValue = 3)]
        public double SellSlPips { get; set; }

        [Parameter("Spread-Toleranz", DefaultValue = 0.5)]
        public double SpreadToleranz { get; set; }

        [Parameter("Start-Stunde UTC", DefaultValue = 6)]
        public int StartStunde { get; set; }
        [Parameter("End-Stunde UTC", DefaultValue = 21)]
        public int EndStunde { get; set; }
        [Parameter("End-Minute UTC", DefaultValue = 54)]
        public int EndMinute { get; set; }

        [Parameter("ActivateLog", DefaultValue = "true")]
        public bool ActivateLog { get; set; }

        private const string BUY_PREFIX = "_buy_";
        private const string SELL_PREFIX = "_sell_";

        private double buyEntryPrice;
        private double sellEntryPrice;

        private bool runningMode = false;
        private bool openCheckDone = false;
        private int openCheckMinute = 0;
        // Braucht es, um zu merken, dass der Tag gelaufen ist, wenn ein TP in der ersten Stunde nach dem Start geschiet.
        private bool tpDoneForToday = false;

        protected override void OnStart()
        {
            if (StartStunde >= EndStunde)
            {
                Print("{0} / ERROR: Start nicht möglich, weil Start-Stunde={1} kleiner als End-Stunde={2} sein muss. Bitte Parameter checken.", SymbolName, StartStunde, EndStunde);
                Stop();
            }

            Positions.Closed += OnPositionClosed;
            Positions.Opened += OnPositionOpened;

            if (ActivateLog)
            {
                Print("{0} / ##Start OldButGoldBot @ bid={1}, ask={2}. Bid is used.", SymbolName, Symbol.Bid, Symbol.Ask);


                if (PendingOrders.Where(p => p.SymbolName == SymbolName).ToList().Count > 0)
                {
                    Print("{0} / INFO->OnStart: PendingOrders at start", SymbolName);
                    foreach (PendingOrder order in PendingOrders.Where(p => p.SymbolName == SymbolName).ToList())
                    {
                        Print("{0} / Still open PendingOrder {1} @ {2}, StopLoss={3}, Volume={4}", SymbolName, order.Label, order.TargetPrice, order.StopLoss, order.VolumeInUnits);
                    }
                }

                if (Positions.Where(p => p.SymbolName == SymbolName).ToList().Count > 0)
                {
                    Print("{0} / INFO->OnStart: Open Positions at start", SymbolName);
                    foreach (Position position in Positions.Where(p => p.SymbolName == SymbolName).ToList())
                    {
                        Print("{0} / Still open Position {1} @ {2}, StopLoss={3}, Pips={4}, Volume={5}", SymbolName, position.Label, position.EntryPrice, position.StopLoss, position.Pips, position.VolumeInUnits);
                    }
                }
            }

            Print("{0} / Params: Volume={1}, Ask={2}, BuyStartPips={3}, Buy-TP={4}, Buy-SL={5}, Bid={6}, SellStartPips={7}, Sell-TP={8}, Sell-SL={9}, Spread={10}, Start-Stunde={11}, End-Stunde={12}, End-Minute={13}", SymbolName, Volume, Symbol.Ask, BuyStartPips, BuyTpPips, BuySlPips, Symbol.Bid, SellStartPips, SellTpPips,
            SellSlPips, GetSpreadPips(), StartStunde, EndStunde, EndMinute);
            Print("{0} / INFO->OnStart: ==============END==================", SymbolName);
        }

        protected override void OnTick()
        {
            // Start des Tages um 06:00 (default). Ist parametrisierbar
            // Ende des Tages um 21:54 (wegen Gold, welches um 21:55 schliesst) (default). Alle offenen Order und Positionen schliessen.

            if (!runningMode && tpDoneForToday)
            {
                // Reset des tpDoneForToday, sobald gestoppt wurde.
                // Braucht es in folgendem Szanario: Die Laufzeit ist zu Ende (Stoptime erreicht) und zur gleichen Zeit gibt es aber noch ein TP.
                // Durch das Ende der Laufzeit wird runningMode = false gesetzt und tpDoneForToday auf false.
                // Weil es aber noch ein TP gibt, wird tpDoneForToday erneut auf true gesetzt und so wird nie mehr gestartet.
                // Daher hier dieses Reset, sobald running = false ist.
                tpDoneForToday = false;
            }

            if (!tpDoneForToday && !runningMode && Server.TimeInUtc.Hour >= StartStunde && Server.TimeInUtc.Hour < EndStunde)
            {
                buyEntryPrice = Symbol.Ask + BuyStartPips * Symbol.PipSize;
                sellEntryPrice = Symbol.Bid - SellStartPips * Symbol.PipSize;

                Print("{0} / INFO->OnTick: Start des Tages. Eröffne neue Orders. Ask={1}, Start-BuyPrice={2}, Bid={3}, Start-SellPrice={4}, Spread={5}", SymbolName, Symbol.Ask, buyEntryPrice, Symbol.Bid, sellEntryPrice, GetSpreadPips());

                runningMode = true;
                // Reinitialisieren.
                openCheckDone = false;
                tpDoneForToday = false;
                // WICHTIG: Sonst geht er direkt in's letzte else if rein und versucht nochmals zu öffnen.
                openCheckMinute = Server.TimeInUtc.Minute;

                OpenOrders();
            }
            else if (runningMode && ((Server.TimeInUtc.Hour == EndStunde && Server.TimeInUtc.Minute >= EndMinute) || (Server.TimeInUtc.Hour == EndStunde + 1)))
            {
                // Weil es ab und zu Tage gibt, wo während mehreresn Minuten kein Tick kommt, darf hier nich tnur auf Hour = EndStunde getestet werden, sondern es muss zusätzlich EndStunde+1 genommen werden...
                // Ist unschön, aber sonst geht das Backtesting nicht
                Print("{0} / INFO->OnTick: Ende des Tages. Schliessen aller offenen Orders und Positionen.", SymbolName);

                // WICHTIG: running setzten, befor Positionen geschlossen werden. Ansonsten würde sie direkt wieder eröffnet werden!!!!!
                runningMode = false;
                tpDoneForToday = false;

                CancelOpenOrders();

                foreach (Position order in Positions.Where(p => p.SymbolName == SymbolName).ToList())
                {
                    order.Close();
                }
            }
            // Wenn nach dem Start keine neuen Order eröffnet werden konnte, weil z.B. der Spread zu diesem Zeitpunkt 
            // zu hoch war, wird hier alle Minuten nochmals versucht, diese Orders zu öffnen.
            // Nur jede Minute und nicht jeden Bar, damit es wegen Async nicht zu Überschneidungen kommt.
            else if (runningMode && !openCheckDone && Server.TimeInUtc.Second == 0 && Server.TimeInUtc.Minute != openCheckMinute)
            {
                bool bothOrdersOpen = true;
                openCheckMinute = Server.TimeInUtc.Minute;

                if (!HasOpenOrderOrPosition(TradeType.Buy))
                {
                    bothOrdersOpen = false;
                    Print("{0} / WARNING->OnTick: Dies sollte eigentlich nie passieren. Der Buy-Order konnten innerhalb einer Minute nicht eröffnet werden. Versuche es erneut.", SymbolName);
                    if ((GetSpreadPips() + SpreadToleranz) >= GetSlPips(TradeType.Buy))
                    {
                        Print("{0} / INFO->OnTick: Spread ist zu gross (Spread={1}). Öffne keinen neuen Order im Moment. Versuche in 1 Minute wieder", SymbolName, GetSpreadPips());
                    }
                    else
                    {
                        OpenOrders();
                    }
                }

                if (!HasOpenOrderOrPosition(TradeType.Sell))
                {
                    bothOrdersOpen = false;
                    Print("{0} / WARNING->OnTick: Dies sollte eigentlich nie passieren. Der Sell-Order konnten innerhalb einer Minute nicht eröffnet werden. Versuche es erneut.", SymbolName);
                    if ((GetSpreadPips() + SpreadToleranz) >= GetSlPips(TradeType.Sell))
                    {
                        Print("{0} / INFO->OnTick: Spread ist zu gross (Spread={1}). Öffne keinen neuen Order im Moment. Versuche in 1 Minute wieder", SymbolName, GetSpreadPips());
                    }
                    else
                    {
                        OpenOrders();
                    }
                }

                // Wichtig: Wenn beide Orders (oder Position) gesetzt sind, den Check stoppen.
                openCheckDone = bothOrdersOpen;
            }
        }


        protected override void OnStop()
        {

            CancelOpenOrders();

            if (ActivateLog)
            {
                double totalOpenPosition = 0;
                foreach (Position position in Positions.Where(p => p.SymbolName == SymbolName).ToList())
                {
                    Print("{0} / INFO->OnStop: Still open position {1} @ {2}, StopLoss={3}, Pips={4}, Volume={5}", SymbolName, position.Label, position.EntryPrice, position.StopLoss, position.Pips, position.VolumeInUnits);
                    totalOpenPosition += position.NetProfit;
                }
                Print("{0} / INFO ###### Total offene Positionen: {1}. Total-Verlust aus noch offenen Positionen: {2}", SymbolName, Positions.Where(p => p.SymbolName == SymbolName).ToList().Count, totalOpenPosition);
            }
        }

        private void CancelOpenOrders()
        {
            if (PendingOrders.Where(p => p.SymbolName == SymbolName).ToList().Count > 0)
            {
                Print("{0} / INFO->CancelOpenOrders: Cancel PendingOrders of current Symbol.", SymbolName);
                foreach (PendingOrder order in PendingOrders.Where(p => p.SymbolName == SymbolName).ToList())
                {
                    order.Cancel();
                }
            }
        }

        private void OpenOrders()
        {
            // Open Orders, wenn es nicht bereits die dazugehörige Position oder den Order gibt
            if (!HasOpenOrderOrPosition(TradeType.Buy))
            {
                PlaceBuyOrderAsync(buyEntryPrice, Volume, BuyTpPips, BuySlPips);
            }

            if (!HasOpenOrderOrPosition(TradeType.Sell))
            {
                PlaceSellOrderAsync(sellEntryPrice, Volume, SellTpPips, SellSlPips);
            }
        }

        private bool HasOpenOrderOrPosition(TradeType tradeType)
        {
            foreach (PendingOrder order in PendingOrders.Where(p => p.SymbolName == SymbolName).ToList())
            {
                if (tradeType == order.TradeType)
                {
                    return true;
                }
            }

            foreach (Position pos in Positions.Where(p => p.SymbolName == SymbolName).ToList())
            {
                if (tradeType == pos.TradeType)
                {
                    return true;
                }
            }

            return false;
        }

        private void PlaceSellOrderAsync(double targetPrice, double volume, double tpPips, double slPips)
        {
            string label = SymbolName + SELL_PREFIX + targetPrice;

            if (targetPrice < Symbol.Ask)
            {
                // Wenn der startValue unter dem aktuellen Kurs ist
                PlaceStopOrderAsync(TradeType.Sell, SymbolName, volume, targetPrice, label, slPips, tpPips, OnAsyncOrderPlaced);
            }
            else
            {
                // Wenn der startValue über dem aktuellen Kurs oder genau gleich ist
                PlaceLimitOrderAsync(TradeType.Sell, SymbolName, volume, targetPrice, label, slPips, tpPips, OnAsyncOrderPlaced);
            }
        }

        private void PlaceBuyOrderAsync(double targetPrice, double volume, double tpPips, double slPips)
        {
            string label = SymbolName + BUY_PREFIX + targetPrice;

            if (targetPrice < Symbol.Bid)
            {
                // Wenn der startValue unter dem aktuellen Kurs ist
                PlaceLimitOrderAsync(TradeType.Buy, SymbolName, volume, targetPrice, label, slPips, tpPips, OnAsyncOrderPlaced);
            }
            else
            {
                // Wenn der startValue über dem aktuellen Kurs oder genau gleich ist
                PlaceStopOrderAsync(TradeType.Buy, SymbolName, volume, targetPrice, label, slPips, tpPips, OnAsyncOrderPlaced);
            }
        }


        private void OnAsyncOrderPlaced(TradeResult tr)
        {
            if (tr.IsSuccessful)
            {
                if (tr.PendingOrder != null)
                {
                    if (ActivateLog)
                    {
                        PendingOrder pendingOrder = tr.PendingOrder;
                        Print("{0} / INFO->OnAsyncOrderPlaced: Der Order wurde gesetzt: {1} @ {2}, TP={3}, SL={4}", SymbolName, pendingOrder.Label, pendingOrder.TargetPrice, pendingOrder.TakeProfitPips, pendingOrder.StopLossPips);
                    }
                }
                else if (tr.Position != null)
                {
                    // Es wurde direkt eine Position aus dem Order
                    if (ActivateLog)
                    {
                        Print("{0} / INFO->OnAsyncOrderPlaced: Der Order wurde direkt zu einer Position: {1}", SymbolName, tr.Position.Label);
                    }
                }
                else
                {
                    Print("{0} / ERROR->OnAsyncOrderPlaced: Hier lief was schief. Im TradeResult ist weder ein PendingOrder, noch eine Position drin, aber das TradeResult ist Successful...", SymbolName);
                }
            }
            else
            {
                Print("{0} / ERROR->OnAsyncOrderPlaced: {1}", SymbolName, tr.Error);
            }
        }

        private void OnPositionOpened(PositionOpenedEventArgs obj)
        {
            Position openedPosition = obj.Position;

            if (openedPosition.SymbolName != SymbolName)
            {
                // MUY importante. Eine Position mit einem anderen Symbol wurde geöffnet. Hier nichts machen.
                return;
            }

            // ----> Dies sollte nie eintreten, da wir nur zwischen 06:00 und 21:54 traden. Code hier einfach als Sicherheit.
            // Es könnte sein, dass es Positionen gibt, die kein SL gesetzt haben.
            // Dieser Fehler kann auftreten, wenn zwischen dem Zeitpunkt wenn der Auftrag zum Öffnen einer
            // Position an den Server geschickt wurde und dem effektiven Eröffnen der Position der Kurs
            // mehr als SL Pips steigt / sinkt. In diesem Fall kommt es zum Error und das SL kann nicht gesetzt
            // werden. Hier räumen wir diese Fälle auf
            if (openedPosition.StopLoss == null)
            {
                double slPips = GetSlPips(openedPosition.TradeType);
                double startDiff = (GetTargetPrice(openedPosition.TradeType) - openedPosition.EntryPrice) / Symbol.PipSize;
                Print("{0} / WARNING->OnPositionOpened: StoppLoss ist null. Spread={1}, SL={2}. Position={3} sollte Preis={4}, hat aber Preis={5} => DiffPips={6}", SymbolName, GetSpreadPips(), slPips, openedPosition.Label, GetTargetPrice(openedPosition.TradeType), openedPosition.EntryPrice, startDiff);

                double slValue;
                double diff;
                if (openedPosition.TradeType == TradeType.Buy)
                {
                    slValue = openedPosition.EntryPrice - Symbol.PipSize * slPips;
                    diff = Symbol.Ask - slValue;
                }
                else
                {
                    slValue = openedPosition.EntryPrice + Symbol.PipSize * slPips;
                    diff = Symbol.Bid - slValue;
                }
                double diffPips = diff / Symbol.PipSize;

                Print("Symbol.PipSize={0}, SLPips={1}, EntryPrice={2}, SlValue={3}, Diff={4}, DiffPips={5}, Label={6}", Symbol.PipSize, slPips, openedPosition.EntryPrice, slValue, diff, diffPips, openedPosition.Label);
                TradeResult tr = ModifyPosition(openedPosition, slValue, openedPosition.TakeProfit);
                if (tr.IsSuccessful)
                {
                    Print("Neu gesetzter SL. Soll={0}, Ist={1}", slValue, tr.Position.StopLoss);
                }
                else
                {
                    Print("{0} / ERROR->OnPositionOpened: Konnte StoppLoss erneut nicht setzen. Schliesse die Position", SymbolName);
                    openedPosition.Close();
                }
            }
            else if (openedPosition.TakeProfit == null)
            {
                double tpPips = GetTpPips(openedPosition.TradeType);
                Print("{0} / WARNING->OnPositionOpened: TakeProfit ist null. Spread={1} ist wohl höher als gesetzer TP={2}. Position {3} wird direkt wieder geschlossen.", SymbolName, GetSpreadPips(), tpPips, openedPosition.Label);
                openedPosition.Close();
            }
            else if (ActivateLog)
            {
                Print("{0} / INFO->OnPositionOpened label={1}, Entry-Price={2}, TP={3}, SL={4}, Spread={5}, id={6}", SymbolName, openedPosition.Label, openedPosition.EntryPrice, openedPosition.TakeProfit, openedPosition.StopLoss, GetSpreadPips(), openedPosition.Id);
            }
        }

        private void OnPositionClosed(PositionClosedEventArgs obj)
        {
            Position closedPosition = obj.Position;

            if (closedPosition.SymbolName != SymbolName)
            {
                // MUY importante. Eine Position mit einem anderen Symbol wurde geschlossen. Hier nichts machen.
                return;
            }

            if (ActivateLog)
            {
                if (closedPosition.GrossProfit >= 0)
                {
                    Print("{0} / $$$ WIN TP->{1} with net profit= {2}", SymbolName, closedPosition.Label, closedPosition.NetProfit);
                    Print("{0} / INFO->OnPositionClosed: Schluss für heute. Cancel von anderem offenen Order.", SymbolName);

                    // ACHTUNG: Hier darf das Flag running NICHT auf false gesetzt werden. Dafür haben wir das tpDoneForToday
                    tpDoneForToday = true;

                    CancelOpenOrders();

                    return;
                }
                else
                {
                    Print("{0} / ------ LOSS / VERLUST SL->{1} with net profit= {2}. Free margin-> {3}", SymbolName, closedPosition.Label, closedPosition.NetProfit, Account.FreeMargin);
                }
            }


            // Bei SL direkt wieder eröffnen
            if (closedPosition.GrossProfit < 0)
            {
                if (!runningMode || tpDoneForToday)
                {
                    // Wenn hoher Spread ist, soll hier kein neuer Order eröffnet werden.
                    Print("{0} / INFO->OnPositionClosed: Wir sind nicht mehr im Running-Mode. Es wird kein automatischer Order mehr für den Loss eröffnet.", SymbolName);
                    return;
                }

                double targetPrice = GetTargetPrice(closedPosition.TradeType);
                double tpPips = GetTpPips(closedPosition.TradeType);
                double slPips = GetSlPips(closedPosition.TradeType);

                if ((GetSpreadPips() + SpreadToleranz) >= slPips)
                {
                    Print("{0} / INFO->OnPositionClosed: Spread ist zu gross (Spread={1}). Öffne keinen neuen Order im Moment. Versuche es in 1 Minute wieder.", SymbolName, GetSpreadPips());
                    openCheckDone = false;
                    return;
                }

                // Genau denselben Order erneut eröffnen, wenn ein Loss eingefahren wurde
                if (closedPosition.TradeType == TradeType.Buy)
                {
                    PlaceBuyOrderAsync(targetPrice, Volume, tpPips, slPips);
                }
                else
                {
                    PlaceSellOrderAsync(targetPrice, Volume, tpPips, slPips);
                }
            }
        }

        private double GetSpreadPips()
        {
            return Symbol.Spread / Symbol.PipSize;
        }

        private double GetTargetPrice(TradeType tradeType)
        {
            return tradeType == TradeType.Buy ? buyEntryPrice : sellEntryPrice;
        }

        private double GetTpPips(TradeType tradeType)
        {
            return tradeType == TradeType.Buy ? BuyTpPips : SellTpPips;
        }

        private double GetSlPips(TradeType tradeType)
        {
            return tradeType == TradeType.Buy ? BuySlPips : SellSlPips;
        }

    }

}

 

I hope I didn't forget anything.

 

In backtesting, everything logs at the exact minute. In the real life, there are seconds. I hope you understand my question.

 

Thank you so much for your help.


@ketos.energy

ketos.energy
21 Dec 2020, 17:03 ( Updated at: 21 Dec 2023, 09:22 )

RE:

PanagiotisCharalampous said:

Hi ketos.energy,

In general, backtesting cannot simulate latency. In backtesting, the entry time is the time at which the tick was generated on the server. In live trading, it takes some time from that moment to the moment your trade is actually executed, since the quote has to travel to your PC, the code needs to be executed, the order needs to be send and then executed on the server. However I cannot know what happens in your case, since the information you provided is very limited.

Best Regards,

Panagiotis 

Join us on Telegram

Hello Panagiotis

I completely understand that there is a latency. But I count with a latency of a few hundert milliseconds, maybe a few secondes in the worst case, but not almost a minute.

Here is the history log of the live trading and the backtesting in the same timeframe.

Live:

Live

 

Backtesting:

Backtesting


@ketos.energy

ketos.energy
21 Dec 2020, 16:28

RE:

PanagiotisCharalampous said:

Hi ketos.energy,

It provides data for every tick.

Best Regards,

Panagiotis 

Join us on Telegram

Thank your for your quick response Panagiotis

How come that I have a shift in every trade of between 4 seconds up to almost one minute?
In backtesting, the positions open at the exact minute (e.g. 04/12/2020 06:00, 04/12/2020 06:04 etc.)
In the live trading however, they start at the same minute, but not the same second apparently (e.g. 04/12/2020 06:00:05.770, 04/12/2020 06:04:12.554 etc.)
 


@ketos.energy

ketos.energy
15 Mar 2019, 10:55

Hi Panagiotis

(Un)Fortunately, I cannot reproduce the error. I will let you know, when the error occurs again.

However, is there a way to still install the version 3.3 and not the beta version? I did not find the old setups.

 

Thank you so much for your very appreciated help.

Adrian


@ketos.energy

ketos.energy
06 Mar 2019, 10:41

Hi Panagiotis

Thanks for your super fast response and link. I really appreciate your service and knowledge.

This makes sense for me. Although, it does happen in over 90% of the closures (see image above), which is a really high number. Is it possible that there is another reason or is there a way to overcome this problem?

Thank you so much.

Adrian


@ketos.energy

ketos.energy
26 Feb 2019, 11:17

Thank you Ton for your response.

According to the API reference, PlaceStopLimitOrder does also take "targetPrice" and not "startPrice" as Parameter -> https://ctrader.com/api/reference/robot/placestoplimitorder


@ketos.energy