C# solution with multiple bots / indicators, a.k.a. monorepo...

Created at 28 Aug 2023, 21:33
How’s your experience with the cTrader Platform?
Your feedback is crucial to cTrader's development. Please take a few seconds to share your opinion and help us improve your trading experience. Thanks!
JO

josef.van.niekerk

Joined 02.02.2023

C# solution with multiple bots / indicators, a.k.a. monorepo...
28 Aug 2023, 21:33


I'm trying to manage building a series of cAutomate indicators and cBots as a mono repository using JetBrains Rider. Currently, I have a C# solution containing multiple projects, it looks something like this:

MyFooBarSolution
  - FooIndicator
  - BarIndicator

The solution lives outside of my cTrader installation, and remarkably, when I build a project, the output .algo file is copied to the correct location on my machine, where my cTrader installation resizes. In other words, my cBot and indicator .algo files are being copied to {my-user-folder}\Documents\cAlgo\Sources\{cBot or Indicators} folder. I manually added the cTrader.automate package, I believe this package contains some magic that copies the binaries to the correct location.

Building the project works just fine. I am also able to Attach to Process, attaching to the algohost process, to initiate a debugging session in JetBrains Rider, once my cBot / Indicator has been added to a chart in cTrader.

Here's the challenge I'm facing; the output .algo files are always generated using the solutions name, and not the project's name it. For example, my .algo files end up being called FooBarSolution.algo for both my FooIndicator and BarIndicator projects.

Is there a way to override this behaviour? I tried manually renaming the files, with the intention of adding a post-build step, however, this causes some weird padding error in cTrader.


@josef.van.niekerk
Replies

josef.van.niekerk
28 Aug 2023, 21:41

I tried updating my Indicator attributes to use the Name attribute like this:

[Indicator(Name = "FooIndicator", AccessRights = AccessRights.FullAccess)]

however, this doesn't work, and Name is marked as obsolete.


@josef.van.niekerk

josef.van.niekerk
28 Aug 2023, 22:36 ( Updated at: 29 Aug 2023, 04:56 )

I know it's weird replying to myself, but I'm adding bits and pieces I'm finding out as I'm going. 

So this documentation page mentions the use of parameters like AlgoName which I tried to change in my project's .csproj file. I can see the effect of AlgoPublish, however, changing AlgoName also has no effect.

Then I came across this forum thread, I tried placing my projects in separate folders within my solution. But when building, my files are still being output as MyFooBarSolution.algo.

I am using the cTrader.Automate package version 1.0.8

Please help?!


@josef.van.niekerk

josef.van.niekerk
30 Aug 2023, 08:39 ( Updated at: 21 Dec 2023, 09:23 )

I'm still stuck with this problem, I need some assistance. My .algo files are still generated with the incorrect name, using the solution name, rather than the project name. I have created a small GitHub repo here, to demonstrate the issue. Check it out using git, and open with JetBrains Rider or Visual Studio.

The GitHub repo contains a small solution, cTraderMonorepo with two indicator projects, SomeIndicator and OtherIndicator. However, when building, as per the screenshot, we can see that in both instances, the output for both projects produce a cTraderMonorepo.algo file, however, I would have expected SomeIndicator.algo and OtherIndicator.algo. The result is, that when building the project, you only end up with one cTraderMonorepo.algo file, I guess the one that gets output last wins.

Another thing I tried was to edit the .csproj files, by adding <AlgoName>OtherIndicator</AlgoName> to the PropertyGroup block. This has no effect at all on the output. According to the  documentation at https://help.ctrader.com/ctrader-automate/compiling/#parameters I'm guessing you should be able to specify the output name for the .algo file using AlgoName.

Maybe @Spotware can help?


@josef.van.niekerk

josef.van.niekerk
30 Aug 2023, 09:04 ( Updated at: 31 Aug 2023, 05:06 )

Furthermore, I tried disabling publishing by setting <AlgoPublish>false</AlgoPublish> in the .csproj file, and adding a post build command On build success using the following command:

xcopy /Y "$(TargetDir)cTraderMonorepo.algo" “%USERPROFILE%\OneDrive\Documents\cAlgo\Sources\Indicators\$(ProjectName).algo”

which doesn't work, it seems the above is trying to run, before the .algo file is output to the filesystem.


@josef.van.niekerk

josef.van.niekerk
30 Aug 2023, 19:26 ( Updated at: 31 Aug 2023, 05:06 )

I have found a workaround, not ideal. By adding the following target to .csproj:

<Target Name="PostBuild" AfterTargets="_BundleAlgo">
  <PropertyGroup>
    <AlgoCopyDestination>%USERPROFILE%\OneDrive\Documents\cAlgo\Sources\Indicators\$(ProjectName).algo</AlgoCopyDestination>
  </PropertyGroup>
  <Exec Command="copy &quot;$(TargetDir)$(SolutionName).algo&quot; &quot;$(AlgoCopyDestination)&quot;"/>
</Target>

and setting <AlgoPublish>false</AlgoPublish>, I'm at least now getting the .algo files where I want them, and with the correct project names.

I should be as easy as updating <AlgoName> instead, but as I mentioned previously, that doesn't work.


@josef.van.niekerk

Spotware
31 Aug 2023, 06:02

Dear Josef,

The described behavior is by design. Indicators and cBots get their name from the solution name. 

Best Regards,

cTrader Team


@Spotware

josef.van.niekerk
31 Aug 2023, 07:28 ( Updated at: 31 Aug 2023, 14:29 )

RE: C# solution with multiple bots / indicators, a.k.a. monorepo...

I find this rather counterproductive. It's not uncommon for .NET solutions to contain multiple projects.

The cTrader documentation at https://help.ctrader.com/ctrader-automate/compiling/#parameters also states clearly that AlgoName is defaulted to $(MSBuildProjectName), it does not say solution name, this documentation is rather misleading. The fact that it's even in the docs implies that it should be overrideable.

I would suggest, AlgoName should work as expected, allowing overriding of the output name. I think I'm not the first one that thinks this way, see the following post: https://ctrader.com/forum/calgo-support/39981#post-99655 

Nonetheless, I've found a workaround by setting AlgoPublish to false, and manually setting up a target to copy the algo with the name I want.

Spotware said: 

Dear Josef,

The described behavior is by design. Indicators and cBots get their name from the solution name. 

Best Regards,

cTrader Team

 


@josef.van.niekerk

devman
05 Sep 2023, 07:46 ( Updated at: 06 Sep 2023, 05:16 )

RE: RE: C# solution with multiple bots / indicators, a.k.a. monorepo...

josef.van.niekerk said: 

I find this rather counterproductive. It's not uncommon for .NET solutions to contain multiple projects.

Try this solution:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <AlgoPublish>false</AlgoPublish>
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="cTrader.Automate" Version="1.0.8" />
    </ItemGroup>

    <Target Name="CopyAlgo"
            Condition="'$(AlgoOut)' != ''"
            DependsOnTargets="Build;$(_CoreAlgoBuildTargets)">
        <ItemGroup>
            <_AlgoFile Include="$(_AlgoFilePath)" />
        </ItemGroup>

        <Copy SourceFiles="@(_AlgoFile)"
              DestinationFolder="$(AlgoOut)"
              OverwriteReadOnlyFiles="True"
              Retries="10"
              RetryDelayMilliseconds="100" />

        <Message Importance="High"
                 Text="$(MSBuildProjectName) -> $(AlgoOut)\$(_AlgoFileName)" />
    </Target>
    
</Project>

Run this command: 

dotnet build -p:AlgoOut=your\path -t:CopyAlgo

 

Of course, you can use the full power of MSBuild and create separate .targets file. Like this:

<!-- FILE: src/copyalgo.target -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="CopyAlgo"
            Condition="'$(AlgoOut)' != ''"
            DependsOnTargets="Build;$(_CoreAlgoBuildTargets)">
        <ItemGroup>
            <_AlgoFile Include="$(_AlgoFilePath)" />
        </ItemGroup>

        <Copy SourceFiles="@(_AlgoFile)"
              DestinationFolder="$(AlgoOut)"
              OverwriteReadOnlyFiles="True"
              Retries="10"
              RetryDelayMilliseconds="100" />

        <Message Importance="High"
                 Text="$(MSBuildProjectName) -> $(AlgoOut)\$(_AlgoFileName)" />
    </Target>
</Project>

Import this file in your projects, or use Directory.Build.props to generalize this customization.

<Project>
  ...
  <Import Project="..\..\src\copyalgo.targets" />
  ...
</Project>

@devman

josef.van.niekerk
06 Sep 2023, 20:56 ( Updated at: 07 Sep 2023, 05:44 )

RE: RE: RE: C# solution with multiple bots / indicators, a.k.a. monorepo...

devman said: 

josef.van.niekerk said: 

I find this rather counterproductive. It's not uncommon for .NET solutions to contain multiple projects.

Try this solution:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <AlgoPublish>false</AlgoPublish>
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="cTrader.Automate" Version="1.0.8" />
    </ItemGroup>

    <Target Name="CopyAlgo"
            Condition="'$(AlgoOut)' != ''"
            DependsOnTargets="Build;$(_CoreAlgoBuildTargets)">
        <ItemGroup>
            <_AlgoFile Include="$(_AlgoFilePath)" />
        </ItemGroup>

        <Copy SourceFiles="@(_AlgoFile)"
              DestinationFolder="$(AlgoOut)"
              OverwriteReadOnlyFiles="True"
              Retries="10"
              RetryDelayMilliseconds="100" />

        <Message Importance="High"
                 Text="$(MSBuildProjectName) -> $(AlgoOut)\$(_AlgoFileName)" />
    </Target>
    
</Project>

Run this command: 

dotnet build -p:AlgoOut=your\path -t:CopyAlgo

 

Of course, you can use the full power of MSBuild and create separate .targets file. Like this:

<!-- FILE: src/copyalgo.target -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="CopyAlgo"
            Condition="'$(AlgoOut)' != ''"
            DependsOnTargets="Build;$(_CoreAlgoBuildTargets)">
        <ItemGroup>
            <_AlgoFile Include="$(_AlgoFilePath)" />
        </ItemGroup>

        <Copy SourceFiles="@(_AlgoFile)"
              DestinationFolder="$(AlgoOut)"
              OverwriteReadOnlyFiles="True"
              Retries="10"
              RetryDelayMilliseconds="100" />

        <Message Importance="High"
                 Text="$(MSBuildProjectName) -> $(AlgoOut)\$(_AlgoFileName)" />
    </Target>
</Project>

Import this file in your projects, or use Directory.Build.props to generalize this customization.

<Project>
  ...
  <Import Project="..\..\src\copyalgo.targets" />
  ...
</Project>

Thanks for taking the time to reply devman. I'll definitely take a look into this. As I mentioned, I have a workaround that works okay for me now, but looking at your code, there's definitely some bits and pieces here that I can learn from and use in my own setup.


@josef.van.niekerk