Replies

solark
04 Jul 2018, 20:32

Sorry just noticed you asked for a button. Was thinking you were looking for something else.


@solark

solark
04 Jul 2018, 20:28

RE:

Untested but try

            var ps = Positions.FindAll("MyLabel", Symbol, tradeType);
            var count = ps.Length;
            var results = ps.Select(p => ClosePositionAsync(p, callback: x => System.Threading.Interlocked.Decrement(ref count))).ToArray();

`Interlocked.Decrement` returns the value after the decrement. So if you need a final callback when all operations have a result just check for when `Interlocked.Decrement` returns zero and call your final callback. You would do the check within the ClosePositionAsync callback but the check would pass exactly once.


@solark

solark
04 Jul 2018, 20:06 ( Updated at: 21 Dec 2023, 09:20 )

They merged. In cTrader click on the curly braces icon on the far left "{}" (in green below)

 

 

 


@solark

solark
04 Jul 2018, 19:57

Minimal example. Compile the following fsx to an assembly


#r @"C:\Users\SomeUser\Documents\cAlgo\API\cAlgo.API.dll"
open cAlgo.API
open System

type AlgoAttribute(name) = 
    inherit Attribute()
    member val Name = name
    member val ParseSource = false with get,set
    member val DestinationDirectory = @"C:\Users\SomeUser\Documents\cAlgo\Sources\Robots\" with get,set
    member val TimeZone = "" with get,set

[<assembly: Algo("StartTest")>]
do()

type Robot2Attribute() = inherit Attribute()

[<Robot2>]
type Current(r : Robot) = 
    member x.OnStart() = r.Print("Hi")

 

 

Create a proxy calgo in ctrader, adding both the above and FSharp.Core as references:

 

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace cAlgo
{
    using System;
    using System.Linq;
    using cAlgo.API;
    using cAlgo.API.Indicators;
    using cAlgo.API.Internals;
    using cAlgo.Indicators;
    using System.Collections.Generic;
    using System.IO;


    [Robot(TimeZone = cAlgo.API.TimeZones.UTC, AccessRights = cAlgo.API.AccessRights.FullAccess)]
    public class StartTest : cAlgo.API.Robot
    {

        private Starttest.Current Current;

        protected override void OnStart()
        {
            Current = new Starttest.Current(this);
            Current.OnStart();
        }

    }
}

 

And get a build error. Only started being an issue after the update.

 


@solark

solark
03 Jul 2018, 23:21

iirc you're gonna pass in an array the same size as `inReal` into `outReal`. outBegIdx will be the first index in which `outReal` is valid. For example an SMA of period 30 will have a outBegIdx of 29 (first index where the SMA can be calculated). `outNBElement`  will be the number of valid entrieds from outBegIdx on. All three can be uninitialized, they're just outputs.


@solark

solark
03 Jul 2018, 23:07

Really depends on your usecase but Ill throw the idea out there cause it might fit for you. You could also just kill the ctrader.exe process from a cbot.


@solark

solark
03 Jul 2018, 23:03

Try using this cbot:

 

https://ctdn.com/algos/cbots/show/588


@solark

solark
03 Jul 2018, 22:45

This version might be a bit quicker this time around....

 

cbotfile is just an ini file (open one in a text editor).

So for example (sorry dont use C# often) you could do

            var l = new List<string>();
            l.Add("[ChartParameters]");
            l.Add("Symbol = " + this.Symbol.Code);
            l.Add("Timeframe = " + this.TimeFrame);

            l.Add("[cBotParameters]");
            foreach (var p in this.GetType().GetProperties().Where(x => x.GetCustomAttributes(typeof(ParameterAttribute), false).Length > 0).Select(x => string.Format("{0} = {1}", x.Name, x.GetValue(this))))
            {
                l.Add(p);
            }
            System.IO.File.WriteAllLines("mycbotsetfile.cbotset", l);

 

If you're creating a Dump method then one parameter would be `this`.

 

Some useful things to log:

  • this.History, I just save to a csv
  • StartTime: r.MarketSeries.OpenTime.[0].ToString("o")
  • EndTime: r.MarketSeries.OpenTime.LastValue.ToString("o")
  • ExperimentTime. DateTime.Not.ToString("o")
  • RunTime: this would require a method call OnStart and tracking a "Dump" object (maybe not worth it). I just use System.Diagnostics.StopWatch
  • Assembly Name/Location
    • var a = System.Reflection.Assembly.GetExecutingAssembly();
      a.AssemblyName;
      a.Location;
  • Using the assembly location you should beable to copy the cs file that created it
  • Market data history: This makes things take longer and really starts to eat up space but is useful for creating your own tools to load this dump files.

 

Turns out for the stats I had to recreate them from the trade history. It's possible the GetFitness method could be used but Id imagine thats only called for genetic optimization.

 

It's convienet to save the files to an archive. You need to add references

#r "System.IO.Compression"
#r "System.IO.Compression.FileSystem

Then the easiest thing is to create a temporary directory (Path.GetTempFileName and Path.GetTempPath are useful here) then write raw files as you normaly would and call `ZipFile.CreateFromDirectory(dir,outFileName)` then clean up.

 

 

 

 


@solark

solark
03 Jul 2018, 22:35

Man... wrote a long post hit submit and just lost it all... This forum is horrible. Ill try rewritting something


@solark

solark
03 Jul 2018, 21:16

Would be a great built in feature. In the meantime Id recommend a simple `dump` method in an external assembly you can just place into OnStop. With the `System.IO.Compression` namepace I actually dump the full marketseries/trade history/stats/cbotset parameters file to a zip to easily load up later. Only thing is to be careful when optimizing (disable it or make sure there's enough disk space)


@solark

solark
03 Jul 2018, 21:01

Seems to me you have

 

Position pos = null;

 

Which gets used unchecked in

        private bool isNearTargetSignal()
        {
            if (pos.TradeType == TradeType.Buy)
            {
                if (sym.Bid > pos.TakeProfit - (sym.PipSize * TriggerPips))
                    return true;
            }
            else if (pos.TradeType == TradeType.Sell)
            {
                if (sym.Ask < pos.TakeProfit + (sym.PipSize * TriggerPips))
                    return true;
            }
            return false;
        }

 


@solark

solark
03 Jul 2018, 20:47

This is more of a general .NET question. Imap access to the email acount is ideal so you can take advantage of the IDLE command (https://en.wikipedia.org/wiki/IMAP_IDLE). Now IMAP is a relatively simple protcol but might as well just take advantage of a library that does the work for you. I quite like IMAPX and they have samples for email notifications here https://imapx.org/docs/wikipage%20(23).html


@solark

solark
03 Jul 2018, 20:41

Easiest would be to just use a local timezone by modifying

 

[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]

 

Now that may cause an issue as I believe JPY adjusts for DST out of sync with UK so it depends on what time you're targeting.  If you'd rather not change that attribute the next best thing is to use the DateTime.ToLocalTime() instance method, perform the time modification and then use the DateTime.ToUniversalTime() instance method if needed.

To acount for various timezones you would do something like

 

var timeZone = TimeZoneInfo.GetSystemTimeZones().Where(x => true).First(); // set an appropriate where condition
var d = new DateTime(someDate.Ticks, DateTimeKind.Unspecified) + someTimeSpan;
var utcTime = TimeZoneInfo.ConvertTimeToUtc(d, timeZone);

 

Hope that helps!


@solark

solark
22 Aug 2017, 23:01

Check out https://github.com/pythonnet/pythonnet and also consider http://accord-framework.net/.


@solark

solark
18 Aug 2017, 00:24

The whole idea of a "hedging account" is kind of pointless, if you're uising FIX the ideal solution is to have a netted acount (opting for a broker that offers such a thing). That would obvisouly allow you to place appropriate stop/limit orders around an open position using FIX.

Assuming this is not an option then the question is more of a general IPC question less so than a Spotware/cAlgo question. The more common methods of IPC are,

1. Sockets over localhost (probably the most popular and opens the possiblity of having processes on separate machines)

2. Named pipes.

3. Shared memory (memory mapped files). With some sort of signaling (named pipes, EventWaitHandle, or whatever)

4. Synchronized file access/ file polling (generally kinda hacky but depending on the situation might be the easiest)

Lastly another option is the Spotware connect API instead of FIX or specifically to modify SL/TP levels.

 

Hope that helps!


@solark

solark
16 Aug 2017, 23:43

Simplest is: (untested but looks right)

 

        protected override void OnStop()
        {
            System.IO.File.WriteAllLines("filename", History.Select(x => String.Format("{0:o},{1}", x.ClosingTime, x.Balance)));
        }

 

You'll want 'filename' to be an absolute path and maybe have the seed in the filename. This only makes datapoints at each trade, if you wanted to include unrealized you'd need to do something a bit different in the OnBar method and dump it OnStop.

Writing them to separate files creates a bit of a chore after the fact to aggregate the data. If python/r/or whatever is your tool of choice to ultimately plot the curves then this should be fine. If it's excel it might be easier to write the data as rows in the same csv (would require some locking to ensure proper writing) and then transpose the data once in excel (and possibly align dates).


@solark

solark
16 Aug 2017, 22:08

I'm pretty sure that's not currently possible. To do what you want to do I'd recommend adding a 'Seed' parameter, initialize the Random object using this parameter, then in the calgo "Optimization" tab, "optimize" over this parameter using "Grid Search" (Not genetic). This will get you a table of results you can then plot elsewhere (excel or whatever). Some notes:

1. Im not sure if the seed method will work properly, it depends on the random number generator. A random number generator with a big space of random numbers (for example Mersenne Twister) performs quite welll using a random seed (chance of 2 iterations running using the same sequence of 'random' numbers is very low). Another solution is to pre-generate a large file of random numbers and syncronize access to the file (the optimizer might run your algo in parallel). I think at 10000 times sequentials seeds should be fine but it's worth mentioning.

2. I can't remember how easy it is to export the table of results out of the optimizer. If it's a pain then just add some code `OnStop` to write the results to a file. If you're looking for equity curves you'll pretty much have to do this but it's not so bad (maybe a couple lines of code).

 

Hope that helps.


@solark

solark
14 May 2016, 19:32

RE:

Override the `GetFitness` method in your cbot so you can be a bit more specific in fitness scoring. That way you know exactly what weighting each feature is getting.


@solark

solark
14 May 2016, 19:20

You override `GetFitness` if you want to use genetic optimization with a custom fitness metric. The `GetFitnessArgs` type provides some useful metrics for creating you're own score. For example if you only cared about your win ratio you could do:

 

        protected override double GetFitness(GetFitnessArgs args)
        {
            
            return args.WinningTrades / (args.WinningTrades + args.LosingTrades + 1);
        }

Though it's nice to get creative and combine rewards/rewards using various metrics to encourage/discourage certain features.

I think whats ultimately being suggested to answer your original question is that although you now have to create a custom fitness function you could also insert logic to check for your minimum stats (most of which are given in `GetFitnessArgs`) and then write the stats to a file as well as the parameters to another (accessible directly).


@solark

solark
14 May 2016, 19:07

The while loop is stalling the main thread. You're gonna want to kick the check off to another thread then get back to the main thread when the condition is met. Try

 

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

            var task = new System.Threading.Tasks.Task(() =>
            {
                while (!pos.StopLoss.HasValue || !pos.TakeProfit.HasValue)
                {
                    Print("Waiting for SL and TP to be assigned to pos {0}...", pos.Id);
                    Thread.Sleep(1000);
                    this.BeginInvokeOnMainThread(() => pos = Positions.Where(p => p.Id == args.Position.Id).First<Position>());
                }
            });
            task.Start();
            Print("Stoploss price of position is {0}", pos.StopLoss.Value);
        }

 

 

So you put the check into a Task, run the task. Once the stop loss and take profit are set then use BeginInvokeOnMainThread to get back to the main cbot thread. Untested and could obviously be cleaned up a bit.


@solark