Topics
14 Mar 2021, 17:53
 2016
 17
19 Jan 2021, 18:33
 1152
 4
Replies

prosteel1
18 Jan 2021, 15:57

RE: RE: RE: RE: RE: RE:

genappsforex said:

Hi Prosteel1,

We did that a few months ago but it was put aside as being counter intuitive (clients might use it wrongly)
The real problem IMO is that there is no real reason why Spotware does not give a wider access to the heart of the backtesting system (it'll only benefit the developers and the traders) as in making to & from date bot controlable, adding ticks history and enabling developers to specify how much bars / ticks should be loaded.
Our library for workarounds, overloads and extentions is getting bigger and bigger and bigger. And also the maintenance effort of it on every new release.

I just hope that not some of the brass puls the plug out of the cTrader project and puts the capacity to the proprietary system again.
 

 

 

 

Yes true, but they are working on major updates atm. .Net Core Upgrade for example :)

I did write some code to attempt to do backtesting in the bot itself while in realtime using methods similar to above, but it wasn't successful - it's actually quite complex to code backtesting lol


@prosteel1

prosteel1
18 Jan 2021, 15:42 ( Updated at: 18 Jan 2021, 15:46 )

RE:

PanagiotisCharalampous said:

Hi 1222Ht,

cTrader Automate doesn't support .Net Standard at the moment.

Best Regards,

Panagiotis 

Join us on Telegram

I believe we are waiting for cTrader 4.0 Beta which should support .Net Core and hopefully this should work? Is that the idea? and any timeframe for the release Panagiotis? I'm expecting it early 2021.

Personally I'm waiting for the .Net Core version to hopefully allow me to use PyTorch as Tensorflow is not the bleeding edge anymore.

From what I've seen it looks like .Net Core supports .Net Standard but not sure tbh.


@prosteel1

prosteel1
18 Jan 2021, 15:27 ( Updated at: 21 Dec 2023, 09:22 )

RE:

Bots4Us said:

Hi cTraders,
Is it possible to determine from w/in a backtest or/and optimization which Data type was used?
I'm not interested in the time frame of the chart, Bars.TimeFrame (e.g. 1h). Instead, I'd like to log which Data type (m1 bars from Server, Tick data from Server, etc.) was used for the backtest/optimization, or (a bit more advanced) determine from within(!) the bot on which Data type tests shall be run.
Thx.

Perhaps get the datetime of consecutive ticks and round it somehow? Perhaps using an average or similar.


@prosteel1

prosteel1
18 Jan 2021, 13:12 ( Updated at: 18 Jan 2021, 14:06 )

RE: RE:

This workaround seems to be working well. I've added my code to the sample trend cbot.

Instructions
The name of the cBot must not contain spaces. This is so the ToString() works.
The cBot needs to be run on each pair until a position is made to determine the Margin per Unit requirement. 1 minute timeframe is quick to make a trade.
When the cBot starts it creates a subdirectory called Margin.
When the cBot is stopped it writes a file specific to the broker, pair and base currency that contains the Margin per Unit.
The next time the cBot starts it reads from this file.
The Margin used is stored in the comment of orders to aid in summing the total margin of orders.

This example is intended to be run on multiple pairs at once and not use more Margin than MarginUsedMax

 

// -------------------------------------------------------------------------------------------------
//
//    This code is a modified cTrader Automate API example.
//
//    This cBot is intended to be used as a sample and does not guarantee any particular outcome or
//    profit of any kind. Use it at your own risk.
//    
//    All changes to this file might be lost on the next application update.
//    If you are going to modify this file please make a copy using the "Duplicate" command.
//
//    The "MargincBot" will buy when fast period moving average crosses the slow period moving average and sell when 
//    the fast period moving average crosses the slow period moving average. The orders are closed when an opposite signal 
//    is generated. There can only by one Buy or Sell order at any time.
//
//    ---- Instructions -----
//    The name of the cBot must not contain spaces. This is so the ToString() works.
//    The cBot needs to be run on each pair until a position is made to determine the Margin per Unit requirement.
//    When the cBot starts it creates a subdirectory called Margin
//    When the cBot is stopped it writes a file specific to the broker, pair and base currency
//    that contains the Margin per Unit.
//    The next time the cBot starts it reads from this file.
//    The Margin used is stored in the comment of orders to aid in summing the total margin of orders.
//
//    This example is intended to be run on multiple pairs at once and not use more Margin than MarginUsedMax
//    
//
// -------------------------------------------------------------------------------------------------

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
using System.IO;

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FileSystem)]
    public class MargincBot : Robot
    {
        [Parameter("Quantity (Lots)", Group = "Volume", DefaultValue = 0.4, MinValue = 0.01, Step = 0.01)]
        public double Quantity { get; set; }

        [Parameter("MA Type", Group = "Moving Average")]
        public MovingAverageType MAType { get; set; }

        [Parameter("Source", Group = "Moving Average")]
        public DataSeries SourceSeries { get; set; }

        [Parameter("Slow Periods", Group = "Moving Average", DefaultValue = 10)]
        public int SlowPeriods { get; set; }

        [Parameter("Fast Periods", Group = "Moving Average", DefaultValue = 5)]
        public int FastPeriods { get; set; }


        private MovingAverage slowMa;
        private MovingAverage fastMa;
        private const string label = "Sample Trend cBot";

        [Parameter("MarginUsedMax", DefaultValue = 0.1, MinValue = 0.1, Step = 0.1)]
        public double MarginUsedMax { get; set; }

        string DataDir;
        string MarginDir;
        string MarginFile;
        double MarginUsed = 0;
        double MarginUsedLast = 0;
        double MarginPerUnit = 0;
        double _margin;

        protected override void OnStart()
        {
            fastMa = Indicators.MovingAverage(SourceSeries, FastPeriods, MAType);
            slowMa = Indicators.MovingAverage(SourceSeries, SlowPeriods, MAType);

            DataDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\Documents\\cAlgo\\Sources\\Robots\\" + ToString() + "\\" + ToString();
            MarginUsedLast = Account.Margin;
            MarginDir = DataDir + "\\Margin\\";
            MarginFile = MarginDir + Account.BrokerName + "-" + Symbol.Name + "-" + Account.Currency + ".csv";

            if (!Directory.Exists(MarginDir))
            {
                Directory.CreateDirectory(MarginDir);
            }
            if (File.Exists(MarginFile) == true)
            {
                Print("MarginFile exists");
                string s;
                using (StreamReader sr = File.OpenText(MarginFile))
                {
                    s = sr.ReadLine();
                    if (s != null)
                    {
                        Print("MarginFile contains: " + s);
                        MarginPerUnit = Convert.ToDouble(s);
                    }
                }
            }
            Positions.Opened += PositionsOnOpened;
            Positions.Closed += PositionsOnClosed;
        }

        protected override void OnTick()
        {
            var longPosition = Positions.Find(label, SymbolName, TradeType.Buy);
            var shortPosition = Positions.Find(label, SymbolName, TradeType.Sell);

            var currentSlowMa = slowMa.Result.Last(0);
            var currentFastMa = fastMa.Result.Last(0);
            var previousSlowMa = slowMa.Result.Last(1);
            var previousFastMa = fastMa.Result.Last(1);

            if (previousSlowMa > previousFastMa && currentSlowMa <= currentFastMa && longPosition == null)
            {
                if (shortPosition != null)
                    ClosePosition(shortPosition);
                _margin = CalculateMargin(VolumeInUnits);
                if (_margin != -1 && _margin + OrdersMargin() < (Math.Min(Account.Balance, Account.Equity) * MarginUsedMax))
                {
                    var Comment = string.Format("{0}", _margin);
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, VolumeInUnits, label, null, null, Comment);
                }
            }
            else if (previousSlowMa < previousFastMa && currentSlowMa >= currentFastMa && shortPosition == null)
            {
                if (longPosition != null)
                    ClosePosition(longPosition);
                _margin = CalculateMargin(VolumeInUnits);
                if (_margin != -1 && _margin + OrdersMargin() < (Math.Min(Account.Balance, Account.Equity) * MarginUsedMax))
                {
                    var Comment = string.Format("{0}", _margin);
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, VolumeInUnits, label, null, null, Comment);
                }
            }
        }

        protected override void OnStop()
        {
            if (RunningMode == RunningMode.RealTime && MarginPerUnit > 0)
            {
                if (File.Exists(MarginFile) == true)
                {
                    // Read file and check if different to MarginFile
                    Print("MarginFile exists");
                    string s;
                    using (StreamReader sr = File.OpenText(MarginFile))
                    {
                        s = sr.ReadLine();
                    }
                    if (s != null && MarginPerUnit > 0 && MarginPerUnit != Convert.ToDouble(s))
                    {
                        Print("MarginFile contains: " + Convert.ToDouble(s) + " which is different to MarginPerUnit = " + MarginPerUnit + " so updating MarginFile");
                        using (StreamWriter sw = File.CreateText(MarginFile))
                        {
                            sw.Write(Math.Round(MarginPerUnit, 5));
                        }
                    }
                }
                else
                {
                    Print("MarginFile does not exist so creating it");
                    using (StreamWriter sw = File.CreateText(MarginFile))
                    {
                        sw.Write(Math.Round(MarginPerUnit, 5));
                    }
                }
            }
        }

        private void PositionsOnOpened(PositionOpenedEventArgs args)
        {
            var pos = args.Position;
            MarginUsed = Account.Margin - MarginUsedLast;
            if (pos.SymbolName == SymbolName)
            {
                if (Account.Margin > MarginUsedLast)
                {
                    MarginPerUnit = Math.Round((MarginUsed / pos.VolumeInUnits), 5);
                    Print("MarginPerUnit = " + MarginPerUnit + ", MarginUsed = " + MarginUsed + ", MarginUsedLast = " + MarginUsedLast + ", pos.VolumeInUnits = " + pos.VolumeInUnits);
                }
            }
            else
            {
                MarginUsedLast = Account.Margin;
            }
        }

        private void PositionsOnClosed(PositionClosedEventArgs args)
        {
            MarginUsedLast = Account.Margin;
        }

        private double VolumeInUnits
        {
            get { return Symbol.QuantityToVolumeInUnits(Quantity); }
        }

        private double OrdersMargin()
        {
            double ordermargin = 0;
            if (PendingOrders.Count > 0)
            {
                foreach (var order in PendingOrders)
                {
                    if (order.Comment != "")
                    {
                        var splitted = order.Comment.Split(' ');
                        double margin = Convert.ToDouble(splitted[0]);
                        if (margin > 0)
                        {
                            ordermargin += margin;
                        }
                    }
                }
            }
            return ordermargin;
        }

        private double CalculateMargin(double Vol)
        {
            if (MarginPerUnit > 0 && Account.Margin + (MarginPerUnit * Vol) <= (Math.Min(Account.Balance, Account.Equity) * MarginUsedMax))
            {
                Print("MarginPerUnit is known = " + MarginPerUnit + ", and the MarginUsed after this trade is = " + (Account.Margin + (MarginPerUnit * Vol)) + ", Which is below " + (Math.Min(Account.Balance, Account.Equity) * MarginUsedMax));
                return Math.Round((MarginPerUnit * Vol), 5);
            }
            else if (MarginPerUnit == 0 && Account.Margin <= (Math.Min(Account.Balance, Account.Equity) * MarginUsedMax))
            {
                Print("MarginPerUnit is not known = " + MarginPerUnit + ", but the MarginUsed before this trade is = " + Account.Margin + ", Which is below " + (Math.Min(Account.Balance, Account.Equity) * MarginUsedMax));
                return 0;
            }
            else
            {
                Print("Trade not made because it would use more than allowed Margin");
                return -1;
            }
        }
    }
}

 


@prosteel1

prosteel1
14 Jan 2021, 18:07 ( Updated at: 14 Jan 2021, 18:11 )

RE:

PanagiotisCharalampous said:

Hi prosteel1,

This information is not available in the API, you will need to calculate it yourself. You can find a useful discussion here.

Best Regards,

Panagiotis 

Join us on Telegram

Thanks for the reply. I saw that but I think it would take 1000 lines of code and still not cover all available pairs of forex, indicies, metals etc.

I'll try my workaround idea as it's probably 50 lines and 1% of the time to code.

 

It is very strange that it is not available in the API as it is in the positions tab, and in the positions info window and the orders info window and even on the mobile app too. The code is there in ctrader but just no entry in the API. 

I think this in an important metric to have as running a cbot with multiple instances and not knowing this could result in an account being wiped out and so it is a major consideration when calculating risk.


@prosteel1

prosteel1
09 Jan 2021, 21:23 ( Updated at: 09 Jan 2021, 22:25 )

RE: RE: RE: RE:

genappsforex said:

Thanks for the answer.
If you ask to release your ban they probably will they're not bad guys, just sometimes a bit too protective ;-)

Sorry to say but i can not make anything from your post.
I have a gmail adress (genappsforex) maybe post it here or mail it to me?


 

bool Booted=false;
DateTime ToDate;
protected override void OnTick()
{
  if (!Booted)
  {
    Booted = true;
    int _loaded = Bars.Count;
    while (ToDate < Bars[0].OpenTime && _loaded > 0)
        _loaded = Bars.LoadMoreHistory();  // Will not work in backtest :-(
  }
}

 

Your using your code which I don't want to spend any time trying to figure out. You are asking the question so I think you should spend a bit of time using my code. My code works. I know it works because I have coded it and tested it. Try my code :) It might be a bit different to your setup so may require some changes.

I know this is a very strange concept but this is a workaround, it's not pretty, it doesn't work great, it is ugly, but it works :)

Loadmorehistory wont work in Backtesting mode, the way you can get history is by starting at an earlier date and letting it run for a while before starting the analysis - this is why I have the         DateTime AnalysisStartDate; variable :)

For instance, when I do this in my bot, I start it in backtesting mode in November 2019 using ontick. And I have my AnalysisStartDate set to 19 march 2020. So it races through the period from November 2019 to march 2020 very quickly, then when it gets to March 19th 2020 it begins the analysis and takes a while to do that, but is has access to all data from November 2019 because that is the date I started it on - I place a condition to only do the analysis after 19th March 2020 is hit and so it has access to 3 months of data!

It is a strange concept I know - it's a workaround though, not supposed to be pretty

I did find that the number of bars on the lower timeframes like Tick10, Minute etc was too large, so I limit the analysis to only the last 2000 bars by using 

if (series[a].Count - 1 >= 2000)
{

// series[a].Count - 2000

}

as the input to my analysis methods for the count to start from.


@prosteel1

prosteel1
09 Jan 2021, 19:55 ( Updated at: 09 Jan 2021, 20:36 )

RE: RE:

genappsforex said:

Thanks for caring but it does not work.

if (RunningMode == RunningMode.RealTime)
skips everything in  OnStart in backtest

 

Yea I think I had another update to post but since I got Perma banned from the Telegram chat for posting a link to this workaround I didn't bother to update it. I haven't been able to ask questions in Telegram since. I guess posting workarounds is a banable offense lol.

I ended up adding another condition Ontick to get around the issue you may be experiencing.... My OnTick() looks like this: By adding the FirstRun check I can run the code I would usually run OnStart() only once. Just copy the OnStart() code into if (FirstRun == 0)

Using this concept we don't want to run our analysis OnStart(), we want to delay the analysis until we have sufficient bars - this is why we are delaying the normal OnStart() code until the Server.Time.Date >= AnalysisStartDate and only once if FirstRun == 0

protected override void OnTick()
        {
            if (RunningMode != RunningMode.RealTime)
            {
                if (Server.Time.Date >= AnalysisStartDate)
                {
                    if (FirstRun == 0)
                    {

 

I hope this helps let me know if it doesn't, as I have a working example, but my posted code might be a bit old and needs to be updated :)


@prosteel1

prosteel1
09 Jan 2021, 17:25 ( Updated at: 09 Jan 2021, 19:39 )

When you have a VPS or dedicated server, the way you connect to it is upto you.

Personally I think the best way is to use a private/public key pair using SSH and I believe this is the standard for securing any server.

I store my private key on an Yubikey from Yubico.com for which I generated a 512 bit RSA key on tails while disconnected from the internet (running off a usb stick) (while wearing a tin foil hat) and then extracted the public key. It took a bit of working out, this is one of the guides I used: 

 

 

Bascially, the security of your VPS or dedicated server is based on the security you set it up to use. My server has password authentication disabled and only accepts a security card such as an Yubikey, and it only accepts an Yubikey with my Private key on it as it checks it against the public key I uploaded to my server - so only I can access it. There is also a rate limit set. It started out as a standard password login server but I changed the settings and uploaded my public key to the public key folder (I'm an IT guy so I googled how to do this).

When I want to access my server I plug my Yubikey into my pc and open Putty which opens an ssh tunnel to my server. It then authenticates me using my Yubikey that is plugged into a usb port and I just type in a 6 digit code to unlock the Yubikey.

 

Your security is based on the way you authenticate to your server, not ctrader. You can do public/Private key authentication without an yubikey or similar, I like a hardware device because it travels with me on my car keys :)

Once the SSH Tunnel is made by Putty I use windows remote desktop connecting to local host and the port set in putty Eg. 127.0.0.1:3388 and so the connection from my local pc to my server is encrypted and secure. I have remote desktop disabled for remote connections but since I have a tunnel I have a local connection so I can connect using remote desktop via the tunnel :) I find Remote Desktop to provide the best experience especially for multiple monitor setups, but also in general the quality is great.

I don't think it is acceptable to be sharing a vps with other users, your broker login details could be accessed by anyone else using the same vps or dedicated server. Best to get your own vps and secure it properly as I mentioned above and not let anyone else have access to it.


@prosteel1

prosteel1
09 Jan 2021, 16:13 ( Updated at: 21 Dec 2023, 09:22 )

RE: RE:

EagleMoxy said:

interesting, good pick up.

 

prosteel1 said:

ICMarkets has BTC/USD with two decimal places and when I tried to change the TP to more than $5000 I got an error.

ICMarkets says it is coded into cTrader. Can this limit please be removed or substancially increased?

 

The main issue is when running a cBot if the TP or SL is more than this limit then the TP or SL wont be set and could result in a trade without a SL.

While I was able to manually set the TP to the price I wanted when the price went down, when it went back up I couldn't change the SL as the TP was more than 500,000 pips.

This also applies to cTrader Desktop.

 

 

Honestly the way that stoploss and take profit is handled in ctrader should be completely re-written. Out of the past 2.5 years that I have been writing my cbot, I have spent 12 months of that figuring out how to prevent trades from going through without a stoploss.

I think the whole concept needs to be re-visited - most of my code is based around checking if a position doesn't have a TP or SL but still I run into situations where this happens. 

 

I do think that Spotware should rework this whole concept because what is in place now simply has so many holes it's not funny.

 

The main issue is that if a TP or SL is sent to the server and it is not accepted there is no way to know if it was successful or not.

 

Should I be doing my position modifications a different way perhaps? I haven't tried using a try or catch.

 

Either I need to change how I write my code or cTrader needs better SL and TP code. Which is it?

 

Even a few days ago I had to write a fix for a bug where if I placed an order with a entry price and tp and sl but while they were all valid at the time the order was placed, at the instant that the order was filled the spread was so large that the ask price hit the long order but the bid was below the SL and so the SL wasn't set, my fix was to close the position. Seriously, still after years I am still finding these bugs where TP and SL are not set.

 

While I keep looking for and finding these TP/SL bugs and writing fixes, I think they should be fixed on the server side because there doesn't seem to be a limit on the number of situations where they can occur. I am finding these situations every week or two atm.

Sorry for the rant, I just don't know where this ends. 


@prosteel1

prosteel1
09 Jan 2021, 15:55

Yes you need to set the IsInteractive = true

if (DrawHorizLines == true)
{
    var rect5 = Chart.DrawHorizontalLine(a + "-" + ShortCounts[a] + "ShortTRF", ShortTRFPrice[a], Color.Red);
    rect5.IsInteractive = true;
}


@prosteel1

prosteel1
09 Jan 2021, 10:58

I posted this 

 

Basically start backtesting from an earlier date and prevent trading until a later date.


@prosteel1

prosteel1
29 Nov 2020, 03:43

RE: RE: RE:

The sample Trend cBot uses the crossover between 2 moving averages. Similar to the Awesome Oscillator which uses the crossover of a 5 and 34 sma. 


@prosteel1

prosteel1
29 Nov 2020, 03:12

RE:

This is a modified RSI bot.

series.Count - 3 gives 2 full bars back, each new bar increases the count by 1.

You can check the print of the High to make sure it is working properly.

 

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

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class SampleRSIcBot : Robot
    {
        [Parameter("Quantity (Lots)", Group = "Volume", DefaultValue = 1, MinValue = 0.01, Step = 0.01)]
        public double Quantity { get; set; }

        [Parameter("Source", Group = "RSI")]
        public DataSeries Source { get; set; }

        [Parameter("Periods", Group = "RSI", DefaultValue = 14)]
        public int Periods { get; set; }

        [Parameter("Partial Close offset", DefaultValue = 10, MinValue = 10, Step = 10)]
        public int Offset { get; set; }

        [Parameter("Timeframe", DefaultValue = "Hour")]
        public TimeFrame tf { get; set; }

        private RelativeStrengthIndex rsi;

        Bars series;
        int BarLast;

        protected override void OnStart()
        {
            rsi = Indicators.RelativeStrengthIndex(Source, Periods);
            series = MarketData.GetBars(tf);
            BarLast = series.Count - 3;
        }
        protected override void OnStop()
        {
            foreach (var position in Positions)
            {
                ClosePosition(position);
            }
        }

        protected override void OnTick()
        {
            if (series.Count - 3 > BarLast)
            {
                Print("series.Count - 3 = " + (series.Count - 3) + " has a High of = " + series.HighPrices[series.Count - 3]);

                if (rsi.Result.LastValue < 30)
                {
                    Close(TradeType.Sell);
                    Open(TradeType.Buy);
                }
                else if (rsi.Result.LastValue > 70)
                {
                    Close(TradeType.Buy);
                    Open(TradeType.Sell);
                }
                if (rsi.Result.LastValue < 30 + Offset)
                {
                    PartialClose(TradeType.Sell);
                }
                if (rsi.Result.LastValue > 70 - Offset)
                {
                    PartialClose(TradeType.Buy);
                }
                BarLast = series.Count - 3;
            }
        }

        private void Close(TradeType tradeType)
        {
            foreach (var position in Positions.FindAll("SampleRSI", SymbolName, tradeType))
                ClosePosition(position);
        }

        private void PartialClose(TradeType tradeType)
        {

            foreach (var position in Positions.FindAll("SampleRSI", SymbolName, tradeType))
            {
                double newVol = Symbol.NormalizeVolumeInUnits((position.VolumeInUnits / 2), RoundingMode.Up);
                if (position.Quantity == Quantity)
                {
                    ClosePosition(position, newVol);
                    Print("Partial closing " + newVol + " of " + position + " due to RSI = " + rsi.Result.LastValue);
                }
            }
        }

        private void Open(TradeType tradeType)
        {
            var position = Positions.Find("SampleRSI", SymbolName, tradeType);
            var volumeInUnits = Symbol.QuantityToVolumeInUnits(Quantity);

            if (position == null)
                ExecuteMarketOrder(tradeType, SymbolName, volumeInUnits, "SampleRSI");
        }
    }
}

 


@prosteel1

prosteel1
28 Nov 2020, 06:38

RE:

duketv said:

Hello,
I am curious if anybody has an idea on how to add multiple take profit levels. Perhaps there already is a snppet or a thread here, but I haven't been able to find it.
Would appreciate any tips :)
Thank You

Duke

 

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

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class SampleRSIcBot : Robot
    {
        [Parameter("Quantity (Lots)", Group = "Volume", DefaultValue = 1, MinValue = 0.01, Step = 0.01)]
        public double Quantity { get; set; }

        [Parameter("Source", Group = "RSI")]
        public DataSeries Source { get; set; }

        [Parameter("Periods", Group = "RSI", DefaultValue = 14)]
        public int Periods { get; set; }

        [Parameter("Partial Close offset", DefaultValue = 10, MinValue = 10, Step = 10)]
        public int Offset { get; set; }

        private RelativeStrengthIndex rsi;

        protected override void OnStart()
        {
            rsi = Indicators.RelativeStrengthIndex(Source, Periods);
        }
        protected override void OnStop()
        {
            foreach (var position in Positions)
            {
                ClosePosition(position);
            }
        }

        protected override void OnTick()
        {
            if (rsi.Result.LastValue < 30)
            {
                Close(TradeType.Sell);
                Open(TradeType.Buy);
            }
            else if (rsi.Result.LastValue > 70)
            {
                Close(TradeType.Buy);
                Open(TradeType.Sell);
            }
            if (rsi.Result.LastValue < 30 + Offset)
            {
                PartialClose(TradeType.Sell);
            }
            if (rsi.Result.LastValue > 70 - Offset)
            {
                PartialClose(TradeType.Buy);
            }
        }

        private void Close(TradeType tradeType)
        {
            foreach (var position in Positions.FindAll("SampleRSI", SymbolName, tradeType))
                ClosePosition(position);
        }

        private void PartialClose(TradeType tradeType)
        {

            foreach (var position in Positions.FindAll("SampleRSI", SymbolName, tradeType))
            {
                double newVol = Symbol.NormalizeVolumeInUnits((position.VolumeInUnits / 2), RoundingMode.Up);
                if (position.Quantity == Quantity)
                {
                    ClosePosition(position, newVol);
                    Print("Partial closing " + newVol + " of " + position + " due to RSI = " + rsi.Result.LastValue);
                }
            }
        }

        private void Open(TradeType tradeType)
        {
            var position = Positions.Find("SampleRSI", SymbolName, tradeType);
            var volumeInUnits = Symbol.QuantityToVolumeInUnits(Quantity);

            if (position == null)
                ExecuteMarketOrder(tradeType, SymbolName, volumeInUnits, "SampleRSI");
        }
    }
}

 


@prosteel1

prosteel1
26 Oct 2020, 20:53

Or if you want to impliment a trailing stoploss or partial take profit then OnTick or OnBar you can search positions for the comment of Short or Long if you set it in your order:

var Shortpos = Positions.FindAll("Short");
foreach (var pos in Shortpos)

{

     if (pos.SymbolName == SymbolName)
        {

            if (pos.StopLoss != ShortTradeSLPrice)
            {
                     ModifyPosition(pos, ShortTradeSLPrice, ShortTradeTPPrice);

            }

      }

}

Note how the ModifyPosition uses prices whereas the orders uses pips - Yes this is stupid lol.

Orders that fill at a different price to the order entry price will have a Stoploss and TakeProfit the set number of pips from the filled price and not the calculated pips from the entry price in the order - my above PositionsOnOpened fixes that bug by checking new positions and setting the stoploss and take profit to the prices in the comment.


@prosteel1

prosteel1
26 Oct 2020, 20:33

ModifyPosition() example

I think this is what you were asking for:

You can also paste this special line in OnStart so it calls the PositionsOnOpened when a Position is opened:

Positions.Opened += PositionsOnOpened;

Then you have a new method: that get run each time a position is opened which can modify stoploss etc. I use the comment to store the entry price, stoploss, take profit etc

private void PositionsOnOpened(PositionOpenedEventArgs args)
{
     var pos = args.Position;

       if (pos.Comment != "")
       {

                var splitted = pos.Comment.Split(' ');
                double ep = Convert.ToDouble(splitted[2]);
                double sl = Convert.ToDouble(splitted[4]);
                double tp = Convert.ToDouble(splitted[5]);
                if (pos.StopLoss != sl || pos.TakeProfit != tp)
                {
                    ModifyPosition(pos, sl, tp);

                }

      }

}


@prosteel1

prosteel1
26 Oct 2020, 19:56

I have found working with SL quite difficult too - mostly because of slippage. I don't think you are trying to fix slippage so I'm guessing it's because of the rounding to pips that is your issue when working with forex and precious metals?

In global I set:

int round = 1;

For rounding to SL in pips I use this check on start to fix the issue of Symbol.Digits for Forex and Symbol.Digits for metals being screwed up:

if (Symbol.PipSize == 0.01 && Symbol.Digits == 2)
{
      round = 0;
 }

Then when placing the order I use this to calculate the stoploss in pips:

double slpips = Math.Round(((x) / Symbol.PipSize), round);

I cant see any way to calculate the rounding figure apart from setting it to 1 for forex and then checking on start if the pipsize and digits requires it to be set to 0. Yes this is stupid lol. And I still have a bug somewhere due to this. This bug makes everything incredibly complex, could easily spend another week on it myself :P So far I have wasted about 1 month of my life on this... so far... more to come lol

 


@prosteel1

prosteel1
26 Oct 2020, 16:38

@samuel.jus.cornelio how did you go? Please ask any more questions you need answered :)

I do have an update to post to my linked post which may be of help but might take a few days. I do like Multi timeframe so feel free to ask :)

 


@prosteel1

prosteel1
26 Oct 2020, 13:00

I got a non trading classifier example by https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/july/test-run-classification-and-prediction-using-neural-networks running from the fourth one down in https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/july/july-2012-code-downloads but the extra classes can't access the calgo Print for some reason  so doesn't print all data to the log.

It's a start to see ML working in cAlgo. It's from this youtube video by a Microsoft dev which seems know his stuff. This is ML written from scratch without API's.

using System;
using System.IO;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FileSystem)]
    public class NeuralNet1 : Robot
    {
        static Random rnd = null;

        protected override void OnStart()
        {
            // Put your initialization logic here
        }

        protected override void OnTick()
        {
            try
            {
                // Put your initialization logic here
                Print("\nBegin neural network classification demo\n");
                Print("Goal is to predict/classify color based on four numeric inputs\n");
                rnd = new Random(159);
                // 159 makes 'good' output
                Print("Creating 100 lines of raw data");
                string dataFile = "..\\..\\colors.txt";
                MakeData(dataFile, 100);

                Print("\nFirst few rows of raw data file are:");
                Helpers.ShowTextFile(dataFile, 4);

                double[][] trainMatrix = null;
                double[][] testMatrix = null;
                Print("\nGenerating train and test matrices using an 80%-20% split");
                MakeTrainAndTest(dataFile, out trainMatrix, out testMatrix);

                Print("\nFirst few rows of training matrix are:");
                Helpers.ShowMatrix(trainMatrix, 5);

                Print("\nCreating 4-input 5-hidden 3-output neural network");
                NeuralNetwork nn = new NeuralNetwork(4, 5, 3);

                Print("Training to find best neural network weights using PSO with cross entropy error");
                double[] bestWeights = nn.Train(trainMatrix);
                Print("\nBest weights found:");
                Helpers.ShowVector(bestWeights, 2, true);

                Print("\nLoading best weights into neural network");
                nn.SetWeights(bestWeights);

                Print("\nAnalyzing the neural network accuracy on the test data\n");
                double accuracy = nn.Test(testMatrix);
                Print("Prediction accuracy = " + accuracy.ToString("F4"));

                Print("\nEnd neural network classification demo\n");
                Console.ReadLine();
            } catch (Exception ex)
            {
                Print("Fatal: " + ex.Message);
                Console.ReadLine();
            }
        }

        protected override void OnStop()
        {
            // Put your deinitialization logic here
        }
        static void MakeData(string dataFile, int numLines)
        {
            double[] weights = new double[] 
            {
                -0.1,
                0.2,
                -0.3,
                0.4,
                -0.5,
                0.6,
                -0.7,
                0.8,
                -0.9,
                1.0,
                -1.1,
                1.2,
                -1.3,
                1.4,
                -1.5,
                1.6,
                -1.7,
                1.8,
                -1.9,
                2.0,
                -0.5,
                0.6,
                -0.7,
                0.8,
                -0.9,
                1.5,
                -1.4,
                1.3,
                -1.2,
                1.1,
                -1.0,
                0.9,
                -0.8,
                0.7,
                -0.6,
                0.5,
                -0.4,
                0.3,
                -0.2,
                0.1,
                0.1,
                -0.3,
                0.6
            };

            NeuralNetwork nn = new NeuralNetwork(4, 5, 3);
            nn.SetWeights(weights);

            FileStream ofs = new FileStream(dataFile, FileMode.Create);
            StreamWriter sw = new StreamWriter(ofs);

            for (int i = 0; i < numLines; ++i)
            {
                double[] inputs = new double[4];
                for (int j = 0; j < inputs.Length; ++j)
                    inputs[j] = rnd.Next(1, 10);

                double[] outputs = nn.ComputeOutputs(inputs);

                string color = "";
                int idx = Helpers.IndexOfLargest(outputs);
                if (idx == 0)
                {
                    color = "red";
                }
                else if (idx == 1)
                {
                    color = "green";
                }
                else if (idx == 2)
                {
                    color = "blue";
                }

                sw.WriteLine(inputs[0].ToString("F1") + " " + inputs[1].ToString("F1") + " " + inputs[2].ToString("F1") + " " + inputs[3].ToString("F1") + " " + color);
            }
            sw.Close();
            ofs.Close();
        }
        // MakeData
        static void MakeTrainAndTest(string file, out double[][] trainMatrix, out double[][] testMatrix)
        {
            int numLines = 0;
            FileStream ifs = new FileStream(file, FileMode.Open);
            StreamReader sr = new StreamReader(ifs);
            while (sr.ReadLine() != null)
                ++numLines;
            sr.Close();
            ifs.Close();

            int numTrain = (int)(0.8 * numLines);
            int numTest = numLines - numTrain;

            double[][] allData = new double[numLines][];
            // could use Helpers.MakeMatrix here
            for (int i = 0; i < allData.Length; ++i)
                allData[i] = new double[7];
            // (x0, x1, x2, x3), (y0, y1, y2)
            string line = "";
            string[] tokens = null;
            ifs = new FileStream(file, FileMode.Open);
            sr = new StreamReader(ifs);
            int row = 0;
            while ((line = sr.ReadLine()) != null)
            {
                tokens = line.Split(' ');
                allData[row][0] = double.Parse(tokens[0]);
                allData[row][1] = double.Parse(tokens[1]);
                allData[row][2] = double.Parse(tokens[2]);
                allData[row][3] = double.Parse(tokens[3]);

                for (int i = 0; i < 4; ++i)
                    allData[row][i] = 0.25 * allData[row][i] - 1.25;
                // scale input data to [-1.0, +1.0]
                if (tokens[4] == "red")
                {
                    allData[row][4] = 1.0;
                    allData[row][5] = 0.0;
                    allData[row][6] = 0.0;
                }
                else if (tokens[4] == "green")
                {
                    allData[row][4] = 0.0;
                    allData[row][5] = 1.0;
                    allData[row][6] = 0.0;
                }
                else if (tokens[4] == "blue")
                {
                    allData[row][4] = 0.0;
                    allData[row][5] = 0.0;
                    allData[row][6] = 1.0;
                }
                ++row;
            }
            sr.Close();
            ifs.Close();

            Helpers.ShuffleRows(allData);

            trainMatrix = Helpers.MakeMatrix(numTrain, 7);
            testMatrix = Helpers.MakeMatrix(numTest, 7);

            for (int i = 0; i < numTrain; ++i)
            {
                allData[i].CopyTo(trainMatrix[i], 0);
            }

            for (int i = 0; i < numTest; ++i)
            {
                allData[i + numTrain].CopyTo(testMatrix[i], 0);
            }
        }
        // MakeTrainAndTest
    }
    class NeuralNetwork
    {
        private int numInput;
        private int numHidden;
        private int numOutput;

        private double[] inputs;
        private double[][] ihWeights;
        // input-to-hidden
        private double[] ihSums;
        private double[] ihBiases;
        private double[] ihOutputs;
        private double[][] hoWeights;
        // hidden-to-output
        private double[] hoSums;
        private double[] hoBiases;
        private double[] outputs;

        static Random rnd = null;

        public NeuralNetwork(int numInput, int numHidden, int numOutput)
        {
            this.numInput = numInput;
            this.numHidden = numHidden;
            this.numOutput = numOutput;

            inputs = new double[numInput];
            ihWeights = Helpers.MakeMatrix(numInput, numHidden);
            ihSums = new double[numHidden];
            ihBiases = new double[numHidden];
            ihOutputs = new double[numHidden];
            hoWeights = Helpers.MakeMatrix(numHidden, numOutput);
            hoSums = new double[numOutput];
            hoBiases = new double[numOutput];
            outputs = new double[numOutput];

            rnd = new Random(0);
        }

        public void SetWeights(double[] weights)
        {
            int numWeights = (numInput * numHidden) + (numHidden * numOutput) + numHidden + numOutput;
            if (weights.Length != numWeights)
                throw new Exception("The weights array length: " + weights.Length + " does not match the total number of weights and biases: " + numWeights);

            int k = 0;
            // points into weights param
            for (int i = 0; i < numInput; ++i)
                for (int j = 0; j < numHidden; ++j)
                    ihWeights[i][j] = weights[k++];

            for (int i = 0; i < numHidden; ++i)
                ihBiases[i] = weights[k++];

            for (int i = 0; i < numHidden; ++i)
                for (int j = 0; j < numOutput; ++j)
                    hoWeights[i][j] = weights[k++];

            for (int i = 0; i < numOutput; ++i)
                hoBiases[i] = weights[k++];
        }

        public double[] ComputeOutputs(double[] currInputs)
        {
            if (inputs.Length != numInput)
                throw new Exception("Inputs array length " + inputs.Length + " does not match NN numInput value " + numInput);

            for (int i = 0; i < numHidden; ++i)
                this.ihSums[i] = 0.0;
            //for (int i = 0; i < numHidden; ++i)
            //  this.ihOutputs[i] = 0.0;
            for (int i = 0; i < numOutput; ++i)
                this.hoSums[i] = 0.0;
            //for (int i = 0; i < numOutput; ++i)
            //  this.outputs[i] = 0.0;


            for (int i = 0; i < currInputs.Length; ++i)
                // copy
                this.inputs[i] = currInputs[i];

            //Console.WriteLine("Inputs:");
            //ShowVector(this.inputs);

            //Console.WriteLine("input-to-hidden weights:");
            //ShowMatrix(this.ihWeights);

            for (int j = 0; j < numHidden; ++j)
                // compute input-to-hidden sums
                for (int i = 0; i < numInput; ++i)
                    ihSums[j] += this.inputs[i] * ihWeights[i][j];

            //Console.WriteLine("input-to-hidden sums:");
            //ShowVector(this.ihSums);

            //Console.WriteLine("input-to-hidden biases:");
            //ShowVector(ihBiases);

            for (int i = 0; i < numHidden; ++i)
                // add biases to input-to-hidden sums
                ihSums[i] += ihBiases[i];

            //Console.WriteLine("input-to-hidden sums after adding biases:");
            //ShowVector(this.ihSums);

            for (int i = 0; i < numHidden; ++i)
                // determine input-to-hidden output
                //ihOutputs[i] = StepFunction(ihSums[i]); // step function
                ihOutputs[i] = SigmoidFunction(ihSums[i]);
            //ihOutputs[i] = TanhFunction(ihSums[i]);

            //Console.WriteLine("input-to-hidden outputs after sigmoid:");
            //ShowVector(this.ihOutputs);

            //Console.WriteLine("hidden-to-output weights:");
            //ShowMatrix(hoWeights);


            for (int j = 0; j < numOutput; ++j)
                // compute hidden-to-output sums
                for (int i = 0; i < numHidden; ++i)
                    hoSums[j] += ihOutputs[i] * hoWeights[i][j];

            //Console.WriteLine("hidden-to-output sums:");
            //ShowVector(hoSums);

            //Console.WriteLine("hidden-to-output biases:");
            //ShowVector(this.hoBiases);

            for (int i = 0; i < numOutput; ++i)
                // add biases to input-to-hidden sums
                hoSums[i] += hoBiases[i];

            //Console.WriteLine("hidden-to-output sums after adding biases:");
            //ShowVector(this.hoSums);

            //for (int i = 0; i < numOutput; ++i)   // determine hidden-to-output result
            //  this.outputs[i] = SigmoidFunction(hoSums[i]);  // step function

            //double[] result = new double[numOutput];
            //this.outputs.CopyTo(result, 0);
            //return result;

            double[] result = Softmax(hoSums);

            result.CopyTo(this.outputs, 0);

            //Console.WriteLine("outputs after softmaxing:");
            //ShowVector(result);

            //Console.ReadLine();

            //double[] result = Hardmax(hoSums);
            return result;
        }
        // ComputeOutputs
        //private static double StepFunction(double x)
        //{
        //  if (x > 0.0) return 1.0;
        //  else return 0.0;
        //}

        private static double SigmoidFunction(double x)
        {
            if (x < -45.0)
                return 0.0;
            else if (x > 45.0)
                return 1.0;
            else
                return 1.0 / (1.0 + Math.Exp(-x));
        }

        private static double[] Softmax(double[] hoSums)
        {
            // determine max
            double max = hoSums[0];
            for (int i = 0; i < hoSums.Length; ++i)
                if (hoSums[i] > max)
                    max = hoSums[i];

            // determine scaling factor (sum of exp(eachval - max)
            double scale = 0.0;
            for (int i = 0; i < hoSums.Length; ++i)
                scale += Math.Exp(hoSums[i] - max);

            double[] result = new double[hoSums.Length];
            for (int i = 0; i < hoSums.Length; ++i)
                result[i] = Math.Exp(hoSums[i] - max) / scale;

            return result;
        }

        // seek and return the best weights
        public double[] Train(double[][] trainMatrix)
        {
            int numWeights = (this.numInput * this.numHidden) + (this.numHidden * this.numOutput) + this.numHidden + this.numOutput;
            //double[] currWeights = new double[numWeights];

            // use PSO to seek best weights
            int numberParticles = 10;
            int numberIterations = 500;
            int iteration = 0;
            int Dim = numWeights;
            // number of values to solve for
            double minX = -5.0;
            // for each weight
            double maxX = 5.0;

            Particle[] swarm = new Particle[numberParticles];
            double[] bestGlobalPosition = new double[Dim];
            // best solution found by any particle in the swarm. implicit initialization to all 0.0
            double bestGlobalFitness = double.MaxValue;
            // smaller values better
            double minV = -0.1 * maxX;
            // velocities
            double maxV = 0.1 * maxX;

            // initialize each Particle in the swarm with random positions and velocities
            for (int i = 0; i < swarm.Length; ++i)
            {
                double[] randomPosition = new double[Dim];
                for (int j = 0; j < randomPosition.Length; ++j)
                {
                    double lo = minX;
                    double hi = maxX;
                    randomPosition[j] = (hi - lo) * rnd.NextDouble() + lo;
                }

                double fitness = CrossEntropy(trainMatrix, randomPosition);
                // smaller values better
                double[] randomVelocity = new double[Dim];

                for (int j = 0; j < randomVelocity.Length; ++j)
                {
                    double lo = -1.0 * Math.Abs(maxX - minX);
                    double hi = Math.Abs(maxX - minX);
                    randomVelocity[j] = (hi - lo) * rnd.NextDouble() + lo;
                }
                swarm[i] = new Particle(randomPosition, fitness, randomVelocity, randomPosition, fitness);

                // does current Particle have global best position/solution?
                if (swarm[i].fitness < bestGlobalFitness)
                {
                    bestGlobalFitness = swarm[i].fitness;
                    swarm[i].position.CopyTo(bestGlobalPosition, 0);
                }
            }
            // initialization
            double w = 0.729;
            // inertia weight.
            double c1 = 1.49445;
            // cognitive/local weight
            double c2 = 1.49445;
            // social/global weight
            double r1, r2;
            // cognitive and social randomizations
            Console.WriteLine("Entering main PSO weight estimation processing loop");
            while (iteration < numberIterations)
            {
                ++iteration;
                double[] newVelocity = new double[Dim];
                double[] newPosition = new double[Dim];
                double newFitness;

                // each Particle
                for (int i = 0; i < swarm.Length; ++i)
                {
                    Particle currP = swarm[i];

                    // each x value of the velocity
                    for (int j = 0; j < currP.velocity.Length; ++j)
                    {
                        r1 = rnd.NextDouble();
                        r2 = rnd.NextDouble();

                        newVelocity[j] = (w * currP.velocity[j]) + (c1 * r1 * (currP.bestPosition[j] - currP.position[j])) + (c2 * r2 * (bestGlobalPosition[j] - currP.position[j]));
                        // new velocity depends on old velocity, best position of parrticle, and best position of any particle
                        if (newVelocity[j] < minV)
                            newVelocity[j] = minV;
                        else if (newVelocity[j] > maxV)
                            newVelocity[j] = maxV;
                        // crude way to keep velocity in range
                    }

                    newVelocity.CopyTo(currP.velocity, 0);

                    for (int j = 0; j < currP.position.Length; ++j)
                    {
                        newPosition[j] = currP.position[j] + newVelocity[j];
                        // compute new position
                        if (newPosition[j] < minX)
                            newPosition[j] = minX;
                        else if (newPosition[j] > maxX)
                            newPosition[j] = maxX;
                    }

                    newPosition.CopyTo(currP.position, 0);

                    newFitness = CrossEntropy(trainMatrix, newPosition);
                    // compute error of the new position
                    currP.fitness = newFitness;

                    // new particle best?
                    if (newFitness < currP.bestFitness)
                    {
                        newPosition.CopyTo(currP.bestPosition, 0);
                        currP.bestFitness = newFitness;
                    }

                    // new global best?
                    if (newFitness < bestGlobalFitness)
                    {
                        newPosition.CopyTo(bestGlobalPosition, 0);
                        bestGlobalFitness = newFitness;
                    }

                }
                // each Particle
                //Console.WriteLine(swarm[0].ToString());
                //Console.ReadLine();

            }
            // while
            Console.WriteLine("Processing complete");
            Console.Write("Final best (smallest) cross entropy error = ");
            Console.WriteLine(bestGlobalFitness.ToString("F4"));

            return bestGlobalPosition;

        }
        // Train
        // (sum) Cross Entropy
        private double CrossEntropy(double[][] trainData, double[] weights)
        {
            // how good (cross entropy) are weights? CrossEntropy is error so smaller values are better
            this.SetWeights(weights);
            // load the weights and biases to examine
            double sce = 0.0;
            // sum of cross entropy
            // walk thru each training case. looks like (6.9 3.2 5.7 2.3) (0 0 1)  where the parens are not really there
            for (int i = 0; i < trainData.Length; ++i)
            {
                double[] currInputs = new double[4];
                currInputs[0] = trainData[i][0];
                currInputs[1] = trainData[i][1];
                currInputs[2] = trainData[i][2];
                currInputs[3] = trainData[i][3];
                double[] currExpected = new double[3];
                currExpected[0] = trainData[i][4];
                currExpected[1] = trainData[i][5];
                currExpected[2] = trainData[i][6];
                // not really necessary
                double[] currOutputs = this.ComputeOutputs(currInputs);
                // run the jnputs through the neural network
                // compute ln of each nn output (and the sum)
                double currSum = 0.0;
                for (int j = 0; j < currOutputs.Length; ++j)
                {
                    if (currExpected[j] != 0.0)
                        currSum += currExpected[j] * Math.Log(currOutputs[j]);
                }
                sce += currSum;
                // accumulate
            }
            return -sce;
        }
        // CrossEntropy
        // returns the accuracy (percent correct predictions)
        public double Test(double[][] testMatrix)
        {
            // assumes that weights have been set using SetWeights
            int numCorrect = 0;
            int numWrong = 0;

            // walk thru each test case. looks like (6.9 3.2 5.7 2.3) (0 0 1)  where the parens are not really there
            for (int i = 0; i < testMatrix.Length; ++i)
            {

                double[] currInputs = new double[4];
                currInputs[0] = testMatrix[i][0];
                currInputs[1] = testMatrix[i][1];
                currInputs[2] = testMatrix[i][2];
                currInputs[3] = testMatrix[i][3];
                double[] currOutputs = new double[3];
                currOutputs[0] = testMatrix[i][4];
                currOutputs[1] = testMatrix[i][5];
                currOutputs[2] = testMatrix[i][6];
                // not really necessary
                double[] currPredicted = this.ComputeOutputs(currInputs);
                // outputs are in softmax form -- each between 0.0, 1.0 representing a prob and summing to 1.0
                //ShowVector(currInputs);
                //ShowVector(currOutputs);
                //ShowVector(currPredicted);

                // use winner-takes all -- highest prob of the prediction
                int indexOfLargest = Helpers.IndexOfLargest(currPredicted);

                // just a few for demo purposes
                if (i <= 3)
                {
                    Console.WriteLine("-----------------------------------");
                    Console.Write("Input:     ");
                    Helpers.ShowVector(currInputs, 2, true);
                    Console.Write("Output:    ");
                    Helpers.ShowVector(currOutputs, 1, false);
                    if (currOutputs[0] == 1.0)
                        Console.WriteLine(" (red)");
                    else if (currOutputs[1] == 1.0)
                        Console.WriteLine(" (green)");
                    else
                        Console.WriteLine(" (blue)");
                    Console.Write("Predicted: ");
                    Helpers.ShowVector(currPredicted, 1, false);
                    if (indexOfLargest == 0)
                        Console.WriteLine(" (red)");
                    else if (indexOfLargest == 1)
                        Console.WriteLine(" (green)");
                    else
                        Console.WriteLine(" (blue)");

                    if (currOutputs[indexOfLargest] == 1)
                        Console.WriteLine("correct");
                    else
                        Console.WriteLine("wrong");
                    Console.WriteLine("-----------------------------------");
                }

                if (currOutputs[indexOfLargest] == 1)
                    ++numCorrect;
                else
                    ++numWrong;

                //Console.ReadLine();
            }
            Console.WriteLine(". . .");

            double percentCorrect = (numCorrect * 1.0) / (numCorrect + numWrong);
            Console.WriteLine("\nCorrect = " + numCorrect);
            Console.WriteLine("Wrong = " + numWrong);

            return percentCorrect;
        }
    }
    public class Helpers
    {
        static Random rnd = new Random(0);

        public static double[][] MakeMatrix(int rows, int cols)
        {
            double[][] result = new double[rows][];
            for (int i = 0; i < rows; ++i)
                result[i] = new double[cols];
            return result;
        }

        public static void ShuffleRows(double[][] matrix)
        {
            for (int i = 0; i < matrix.Length; ++i)
            {
                int r = rnd.Next(i, matrix.Length);
                double[] tmp = matrix[r];
                matrix[r] = matrix[i];
                matrix[i] = tmp;
            }
        }

        public static int IndexOfLargest(double[] vector)
        {
            int indexOfLargest = 0;
            double maxVal = vector[0];
            for (int i = 0; i < vector.Length; ++i)
            {
                if (vector[i] > maxVal)
                {
                    maxVal = vector[i];
                    indexOfLargest = i;
                }
            }
            return indexOfLargest;
        }

        public static void ShowVector(double[] vector, int decimals, bool newLine)
        {
            string fmt = "F" + decimals;
            for (int i = 0; i < vector.Length; ++i)
            {
                if (i > 0 && i % 12 == 0)
                    Console.WriteLine("");
                if (vector[i] >= 0.0)
                    Console.Write(" ");
                Console.Write(vector[i].ToString(fmt) + " ");
            }
            if (newLine == true)
                Console.WriteLine("");
        }

        public static void ShowMatrix(double[][] matrix, int numRows)
        {
            int ct = 0;
            if (numRows == -1)
                numRows = int.MaxValue;
            for (int i = 0; i < matrix.Length && ct < numRows; ++i)
            {
                for (int j = 0; j < matrix[0].Length; ++j)
                {
                    if (matrix[i][j] >= 0.0)
                        Console.Write(" ");
                    if (j == 4)
                        Console.Write("-> ");
                    Console.Write(matrix[i][j].ToString("F2") + " ");
                }
                Console.WriteLine("");
                ++ct;
            }
            Console.WriteLine("");
        }

        public static void ShowTextFile(string textFile, int numLines)
        {
            FileStream ifs = new FileStream(textFile, FileMode.Open);
            StreamReader sr = new StreamReader(ifs);
            string line = "";
            int ct = 0;
            while ((line = sr.ReadLine()) != null && ct < numLines)
            {
                Console.WriteLine(line);
                ++ct;
            }
            sr.Close();
            ifs.Close();
        }
    }
    public class Particle
    {
        public double[] position;
        // equivalent to x-Values and/or solution
        public double fitness;
        public double[] velocity;

        public double[] bestPosition;
        // best position found so far by this Particle
        public double bestFitness;

        public Particle(double[] position, double fitness, double[] velocity, double[] bestPosition, double bestFitness)
        {
            this.position = new double[position.Length];
            position.CopyTo(this.position, 0);
            this.fitness = fitness;
            this.velocity = new double[velocity.Length];
            velocity.CopyTo(this.velocity, 0);
            this.bestPosition = new double[bestPosition.Length];
            bestPosition.CopyTo(this.bestPosition, 0);
            this.bestFitness = bestFitness;
        }

        public override string ToString()
        {
            string s = "";
            s += "==========================\n";
            s += "Position: ";
            for (int i = 0; i < this.position.Length; ++i)
                s += this.position[i].ToString("F2") + " ";
            s += "\n";
            s += "Fitness = " + this.fitness.ToString("F4") + "\n";
            s += "Velocity: ";
            for (int i = 0; i < this.velocity.Length; ++i)
                s += this.velocity[i].ToString("F2") + " ";
            s += "\n";
            s += "Best Position: ";
            for (int i = 0; i < this.bestPosition.Length; ++i)
                s += this.bestPosition[i].ToString("F2") + " ";
            s += "\n";
            s += "Best Fitness = " + this.bestFitness.ToString("F4") + "\n";
            s += "==========================\n";
            return s;
        }
    }
}

 


@prosteel1

prosteel1
23 Oct 2020, 20:07

RE:

afhacker said:

cTrader will migrate to .NET core and in future you will be able to use ML.NET, but if you are a serious algo trader and want to use ML on your algos then there is no need to wait, you don't have to use even .NET at all for your ML stuff, use Keras or any other ML library like Sklearn with Spotware Open API.

You can also use ML.NET with Open API, I use Python when it comes to ML and for trading I use Open API.

If you don't want to use Open API or the authentication is hard for you to implement you can use Python ML libs for building your model and then you can send the ML model signal to a cBot with a socket connection.

I love .NET but for ML its not so mature right now. 

 

Wow thanks for the answer to my question "@PanagiotisCharalampous mentioned cTrader 4.0 would be upgraded to  .NetCore, would that upgrade allow it to work?

While I am serious about my algos I doubt I'm good enough to use Spotware Open API, I know it's available but sounds like I would need to be a proper developer to use it, while I'm just a hack that's been learning C# for the past 2.5 years simply because the cTrader team seem to provide the best support of any trading platform.

Your description of how you use it is very helpful and I appreciate you replying with great info.

I've been liking the advances in Tensorflow lately. The future looks good for using machine learning in algos!

 

Since I have all of my code in cTrader and only looking to impliment ML in the trading decisions code, which I am about to re-write, perhaps I could use ML for the decisions and Spotware OpenAPI to place the trades, I'll have a look and thanks for the suggestion :)

I think I've just taken the blue pill and about to see for far down the .NET rabbit hole goes lol.


@prosteel1