BacktestingDataSources
interface cAlgo.API Since 5.3
DesktopCLICloud
Provides access to backtesting data sources.
Environment behaviour
CLI — not supported. Available to plugins only, and plugins cannot run in the CLI.
Cloud — not supported. Available to plugins only, and plugins cannot run in the cloud.
Definition
public interface BacktestingDataSources
Namespace: cAlgo.API · Group: DataSource
Examples
Example 1
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using System.Linq;
namespace cAlgo.Plugins;
public partial class CustomDataSourceTest : Plugin
{
private BacktestingDataSource _dataSource;
protected override void OnStart()
{
var addCustomDataSource = new Button { Text = "Add Custom Data Source" };
addCustomDataSource.Click += OnAddCustomDataSourceClick;
var removeCustomDataSource = new Button { Text = "Remove Custom Data Source" };
removeCustomDataSource.Click += OnRemoveCustomDataSourceClick;
var panel = new StackPanel();
panel.AddChild(addCustomDataSource);
panel.AddChild(removeCustomDataSource);
var aspBlock = Asp.SymbolTab.AddBlock("Custom Data Source Test");
aspBlock.Child = panel;
}
private void OnRemoveCustomDataSourceClick(ButtonClickEventArgs obj)
{
Backtesting.DataSources.Remove(_dataSource);
}
private void OnAddCustomDataSourceClick(ButtonClickEventArgs obj)
{
var options = new BacktestingDataSourceOptions(BacktestingDataSourceDataType.Tick | BacktestingDataSourceDataType.M1OpenPrices | BacktestingDataSourceDataType.OpenPrices, OnMinMaxTimeRequested, OnDataRequested);
_dataSource = Backtesting.DataSources.Add("Test", options);
}
private void OnDataRequested(BacktestingDataRequest request)
{
request.StatusChanged += OnRequestStatusChanged;
Print($"Data Request from {request.StartTime} to {request.EndTime}");
switch (request.DataType)
{
case BacktestingDataSourceDataType.Tick:
var ticks = GetAllTicksTill(request.PriceDataSourceId.Symbol.Name, request.EndTime);
var tickData = ticks.Where(b => b.Time >= request.StartTime && b.Time <= request.EndTime).Select(t => new BacktestingTick(t.Time, t.Bid, t.Ask)).ToArray();
Print($"Sending {tickData.Length} ticks.");
request.Complete(new BacktestingTickData(tickData));
break;
case BacktestingDataSourceDataType.M1OpenPrices:
var minuteBars = GetAllBars(TimeFrame.Minute, request.PriceDataSourceId.Symbol.Name);
var minuteData = minuteBars
.Where(b => b.OpenTime >= request.StartTime && b.OpenTime <= request.EndTime).Select(t =>
new BacktestingBar(t.OpenTime, t.Open, t.High, t.Low, t.Close, (long)t.TickVolume)).ToArray();
Print($"Sending {minuteData.Length} minute bars.");
request.Complete(new BacktestingBarsData(minuteData));
break;
case BacktestingDataSourceDataType.OpenPrices:
var bars = GetAllBars(request.PriceDataSourceId.TimeFrame, request.PriceDataSourceId.Symbol.Name);
var barsData = bars
.Where(b => b.OpenTime >= request.StartTime && b.OpenTime <= request.EndTime).Select(t =>
new BacktestingBar(t.OpenTime, t.Open, t.High, t.Low, t.Close, (long)t.TickVolume)).ToArray();
Print($"Sending {barsData.Length} bars.");
request.Complete(new BacktestingBarsData(barsData));
break;
default:
request.Fail($"Data type '{request.DataType}' not supported.");
break;
}
}
private void OnRequestStatusChanged(BacktestingDataRequestStatusChangedEventArgs args)
{
Print($"Request '{args.DataRequest.PriceDataSourceId}' status changed: {args.DataRequest.Status}");
}
private Bars GetAllBars(TimeFrame timeFrame, string symbolName)
{
var bars = MarketData.GetBars(timeFrame, symbolName);
while (bars.LoadMoreHistory() > 0)
{
}
Print($"Bars loaded, count: {bars.Count}");
return bars;
}
private Ticks GetAllTicksTill(string symbolName, DateTime tillTime)
{
var ticks = MarketData.GetTicks(symbolName);
while (ticks.LoadMoreHistory() > 0 && ticks[0].Time >= tillTime)
{
}
Print($"Ticks loaded, count: {ticks.Count}");
return ticks;
}
private BacktestingMinMaxTime OnMinMaxTimeRequested(BacktestingDataSourceMinMaxTimeArgs args)
{
return new BacktestingMinMaxTime(Server.Time.AddYears(-2), Server.Time);
}
}
import clr
clr.AddReference("cAlgo.API")
from cAlgo.API import *
from System import Action, Func, Array
class Test():
def on_start(self):
self.dataSource = None
addCustomDataSource = Button()
addCustomDataSource.Text = "Add Custom Data Source"
addCustomDataSource.Click += self.on_add_custom_data_source_click
removeCustomDataSource = Button()
removeCustomDataSource.Text = "Remove Custom Data Source"
removeCustomDataSource.Click += self.on_remove_custom_data_source_click
panel = StackPanel()
panel.AddChild(addCustomDataSource)
panel.AddChild(removeCustomDataSource)
aspBlock = api.Asp.SymbolTab.AddBlock("Custom Data Source Test")
aspBlock.Child = panel
def on_add_custom_data_source_click(self, args):
options = BacktestingDataSourceOptions(BacktestingDataSourceDataType.Tick | BacktestingDataSourceDataType.M1OpenPrices | BacktestingDataSourceDataType.OpenPrices, Func[BacktestingDataSourceMinMaxTimeArgs, BacktestingMinMaxTime](self.on_min_max_time_requested), Action[BacktestingDataRequest](self.on_data_requested))
self.dataSource = api.Backtesting.DataSources.Add("Test", options)
def on_remove_custom_data_source_click(self, args):
if self.dataSource is not None:
api.Backtesting.DataSources.Remove(self.dataSource)
def on_min_max_time_requested(self, args):
return BacktestingMinMaxTime(api.Server.Time.AddYears(-2), api.Server.Time)
def on_data_requested(self, request):
request.StatusChanged += self.on_request_status_changed
print(f"Data Request from {request.StartTime} to {request.EndTime}")
match request.DataType:
case BacktestingDataSourceDataType.Tick:
ticks = self.get_all_ticks(request.PriceDataSourceId.Symbol.Name, request.EndTime)
tickData = self.to_array([BacktestingTick(t.Time, t.Bid, t.Ask) for t in ticks if t.Time >= request.StartTime and t.Time <= request.EndTime], BacktestingTick)
print(f"Sending {len(tickData)} ticks.")
request.Complete(BacktestingTickData(tickData))
case BacktestingDataSourceDataType.M1OpenPrices:
minuteBars = self.get_all_bars(TimeFrame.Minute, request.PriceDataSourceId.Symbol.Name)
minuteData = self.to_array([BacktestingBar(b.OpenTime, b.Open, b.High, b.Low, b.Close, b.TickVolume) for b in minuteBars if b.OpenTime >= request.StartTime and b.OpenTime <= request.EndTime], BacktestingBar)
print(f"Sending {len(minuteData)} minute bars.")
request.Complete(BacktestingBarsData(minuteData))
case BacktestingDataSourceDataType.OpenPrices:
bars = self.get_all_bars(request.PriceDataSourceId.TimeFrame, request.PriceDataSourceId.Symbol.Name)
barsData = self.to_array([BacktestingBar(b.OpenTime, b.Open, b.High, b.Low, b.Close, b.TickVolume) for b in bars if b.OpenTime >= request.StartTime and b.OpenTime <= request.EndTime], BacktestingBar)
print(f"Sending {len(barsData)} bars.")
request.Complete(BacktestingBarsData(barsData))
case _:
request.Fail(f"Data type '{request.DataType}' not supported.")
def on_request_status_changed(self, args):
print(f"Request '{args.DataRequest.PriceDataSourceId}' status changed: {args.DataRequest.Status}")
def get_all_ticks(self, symbolName, tillTime):
ticks = api.MarketData.GetTicks(symbolName)
while ticks.LastTick.Time < tillTime and ticks.LoadMoreHistory() > 0:
pass
print(f"Ticks loaded, count: {ticks.Count}")
return ticks
def get_all_bars(self, timeFrame, symbolName):
bars = api.MarketData.GetBars(timeFrame, symbolName)
while bars.LoadMoreHistory() > 0:
pass
print(f"Bars loaded, count: {bars.Count}")
return bars
def to_array(self, iterable, itemType):
length = len(iterable)
array = Array[itemType](length)
for i in range(length):
array[i] = iterable[i]
return array
{
"AccessRights": "None"
}
Example 2
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using cAlgo.API;
using cAlgo.API.Internals;
using System.Linq;
namespace cAlgo.Plugins;
public partial class CustomDataSourceCSVTest : Plugin
{
private BacktestingDataSource _dataSource;
private TextBox _symbolNameTextBox;
private ComboBox _timeFrameCombobox;
private string _selectedFilePath;
private readonly Dictionary<DataId, Data> _addedData = new();
private readonly Dictionary<DataId, Dictionary<DateOnly, List<BacktestingTick>>> _cachedTickData = new();
private readonly Dictionary<DataId, List<BacktestingBar>> _cachedBarData = new();
private StackPanel _panel;
private Button _selectFileButton;
private CustomTimeFrame _customTimeFrame;
protected override void OnStart()
{
LoadFromLocalStorage();
var options = new BacktestingDataSourceOptions(BacktestingDataSourceDataType.Tick | BacktestingDataSourceDataType.M1OpenPrices | BacktestingDataSourceDataType.OpenPrices, OnMinMaxTimeRequested, OnDataRequested);
_dataSource = Backtesting.DataSources.Add("CSV", options);
var aspBlock = Asp.SymbolTab.AddBlock("Custom Data Source CSV Test");
aspBlock.Child = GetAddDataPanel();
aspBlock.Height = 400;
}
private Panel GetAddDataPanel()
{
_panel = new StackPanel() { Orientation = Orientation.Vertical, Margin = 3 };
_symbolNameTextBox = new TextBox() { Text = "Enter Symbol Name", Margin = 3 };
_panel.AddChild(_symbolNameTextBox);
_timeFrameCombobox = new ComboBox() { Margin = 3, Padding = 2 };
foreach (var timeFrame in GetTimeFrames())
{
_timeFrameCombobox.AddItem(timeFrame.Name);
}
_timeFrameCombobox.SelectedItem = TimeFrame.Minute.Name;
_panel.AddChild(_timeFrameCombobox);
_selectFileButton = new Button() { Text = "Select File", Margin = 3 };
_selectFileButton.Click += OnSelectFileButtonClicked;
_panel.AddChild(_selectFileButton);
var addButton = new Button() { Text = "Add Data", Margin = 3 };
addButton.Click += OnAddClick;
_panel.AddChild(addButton);
var showDataButton = new Button() { Text = "Show Data", Margin = 3 };
showDataButton.Click += OnShowDataClick;
_panel.AddChild(showDataButton);
var removeAllDataButton = new Button() { Text = "Remove All Data", Margin = 3 };
removeAllDataButton.Click += OnRemoveAllDataClick;
_panel.AddChild(removeAllDataButton);
var clearCacheButton = new Button() { Text = "Clear Cache", Margin = 3 };
clearCacheButton.Click += OnClearCacheClick;
_panel.AddChild(clearCacheButton);
var addCustomTimeFrameButton = new Button() { Text = "Add Custom Time Frame", Margin = 3 };
addCustomTimeFrameButton.Click += OnAddCustomTimeFrameClick;
_panel.AddChild(addCustomTimeFrameButton);
var removeCustomTimeFrameButton = new Button() { Text = "Remove Custom Time Frame", Margin = 3 };
removeCustomTimeFrameButton.Click += OnRemoveCustomTimeFrameClick;
_panel.AddChild(removeCustomTimeFrameButton);
return _panel;
}
private void OnRemoveCustomTimeFrameClick(ButtonClickEventArgs obj)
{
if (TimeFrameManager.Custom.Remove(_customTimeFrame))
{
Print($"Custom time frame removed.");
return;
}
Print($"Removing custom time frame failed.");
}
private void OnAddCustomTimeFrameClick(ButtonClickEventArgs obj)
{
_customTimeFrame = TimeFrameManager.Custom.Add("Custom Data Source Test (H1)");
_customTimeFrame.BarsNeeded = args =>
{
Print($"Custom time frame bars needed for symbol '{args.CustomBars.Symbol.Name}'.");
var bars = MarketData.GetBars(TimeFrame.Hour, args.CustomBars.Symbol.Name);
Print($"Appending custom time frame bars '{bars.Count}' for symbol '{args.CustomBars.Symbol.Name}'.");
args.CustomBars.AppendBars(bars.Select(b => new CustomBar(b.OpenTime, b.Open, b.High, b.Low, b.Close, (long)b.TickVolume)));
};
Print("Custom time frame added.");
}
private void OnClearCacheClick(ButtonClickEventArgs obj)
{
if (_dataSource.ClearCache())
{
Print($"Data source '{_dataSource.Name}' cached data cleared.");
return;
}
Print($"Data source '{_dataSource.Name}' cached data clear failed.");
}
private void OnRemoveAllDataClick(ButtonClickEventArgs obj)
{
foreach (var (_, data) in _addedData)
{
System.IO.File.Delete(data.FilePath);
}
LocalStorage.Remove("Data", LocalStorageScope.Type);
LocalStorage.Flush(LocalStorageScope.Type);
_addedData.Clear();
Print("All added data has been removed.");
}
private void OnAddClick(ButtonClickEventArgs obj)
{
if (_symbolNameTextBox.Text.Length == 0 || Symbols.GetSymbol(_symbolNameTextBox.Text) is not { } symbol)
{
Print($"Invalid or empty symbol name: {_symbolNameTextBox.Text}");
return;
}
if (string.IsNullOrWhiteSpace(_timeFrameCombobox.SelectedItem) || !TimeFrame.TryParse(_timeFrameCombobox.SelectedItem, out var selectedTimeFrame))
{
Print($"Invalid time frame: {_timeFrameCombobox.SelectedItem}");
return;
}
if (string.IsNullOrWhiteSpace(_selectedFilePath))
{
Print($"Invalid file path: {_selectedFilePath}");
return;
}
var dataType = selectedTimeFrame == TimeFrame.Tick
? BacktestingDataSourceDataType.Tick
: selectedTimeFrame == TimeFrame.Minute
? BacktestingDataSourceDataType.M1OpenPrices
: BacktestingDataSourceDataType.OpenPrices;
var dataId = new DataId(symbol.Name, selectedTimeFrame.Name, dataType);
_panel.IsEnabled = false;
BeginInvokeOnMainThread(() =>
{
try
{
var minMaxTime = GetMinMaxTime(_selectedFilePath);
var data = new Data(_selectedFilePath, minMaxTime);
_addedData[dataId] = data;
SaveToLocalStorage(dataId, data);
Print($"Data for symbol {symbol} and time frame {selectedTimeFrame} is added: {_selectedFilePath}, Min/Max Time: {minMaxTime}");
}
finally
{
_panel.IsEnabled = true;
}
});
}
private void SaveToLocalStorage(DataId dataId, Data data)
{
var existingData = LocalStorage.GetObject<List<(DataId DataId, Data Data)>>("Data", LocalStorageScope.Type)
?? new List<(DataId DataId, Data Data)>();
existingData.Add((dataId, data));
LocalStorage.SetObject("Data", existingData, LocalStorageScope.Type);
LocalStorage.Flush(LocalStorageScope.Type);
}
private void OnShowDataClick(ButtonClickEventArgs obj)
{
var existingData =
LocalStorage.GetObject<List<(DataId DataId, Data Data)>>("Data", LocalStorageScope.Type);
if (existingData is null)
return;
Print("Showing data");
foreach (var (dataId, data) in existingData)
{
Print($"Data '{dataId}' with '{data}'");
}
}
private void LoadFromLocalStorage()
{
var existingData =
LocalStorage.GetObject<List<(DataId DataId, Data Data)>>("Data", LocalStorageScope.Type);
if (existingData is null)
return;
foreach (var (dataId, data) in existingData)
{
_addedData[dataId] = data with
{
MinMaxTime = data.MinMaxTime with
{
MinTime = DateTime.SpecifyKind(data.MinMaxTime.MinTime, DateTimeKind.Utc),
MaxTime = DateTime.SpecifyKind(data.MinMaxTime.MaxTime, DateTimeKind.Utc)
}
};
Print($"Added data '{dataId}' from local storage");
}
}
private void OnSelectFileButtonClicked(ButtonClickEventArgs obj)
{
var openFileDialog = new OpenFileDialog();
_panel.IsEnabled = false;
_selectFileButton.Text = "Please wait...";
BeginInvokeOnMainThread(() =>
{
try
{
var result = openFileDialog.ShowDialog();
if (result != FileDialogResult.OK)
return;
_selectedFilePath = openFileDialog.FileName;
}
finally
{
_selectFileButton.Text = "Select File";
_panel.IsEnabled = true;
}
});
}
private IEnumerable<TimeFrame> GetTimeFrames()
{
var timeFrameFields = typeof(TimeFrame).GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (var timeFrameField in timeFrameFields)
{
if (timeFrameField.GetValue(null) is not TimeFrame timeFrame)
continue;
yield return timeFrame;
}
}
private void OnDataRequested(BacktestingDataRequest request)
{
request.StatusChanged += OnRequestStatusChanged;
Print($"Data Request from {request.StartTime} to {request.EndTime}");
var dataId = GetDataId(request.PriceDataSourceId, request.DataType);
if (!_addedData.TryGetValue(dataId, out var data))
{
request.Fail($"Data not found for '{dataId}'.");
return;
}
switch (request.DataType)
{
case BacktestingDataSourceDataType.Tick:
var ticks = GetRequestTicks(dataId, data, request);
Print($"Sending ticks.");
request.Complete(ticks);
break;
default:
var bars = GetRequestBars(dataId, data, request);
Print($"Sending bars.");
request.Complete(bars);
break;
}
}
private BacktestingData GetRequestTicks(DataId dataId, Data data, BacktestingDataRequest request)
{
if (!_cachedTickData.TryGetValue(dataId, out var tickData))
{
tickData = CacheTickData(dataId, data);
_cachedTickData[dataId] = tickData;
}
var requestData = new List<BacktestingTick>();
for (var i = DateOnly.FromDateTime(request.StartTime); i <= DateOnly.FromDateTime(request.EndTime); i = i.AddDays(1))
{
if (!tickData.TryGetValue(i, out var ticks))
continue;
requestData.AddRange(ticks);
}
return new BacktestingTickData(requestData);
}
private BacktestingData GetRequestBars(DataId dataId, Data data, BacktestingDataRequest request)
{
if (!_cachedBarData.TryGetValue(dataId, out var barsData))
{
barsData = CacheBarData(dataId, data);
_cachedBarData[dataId] = barsData;
}
return new BacktestingBarsData(barsData.Where(b => b.Time >= request.StartTime && b.Time <= request.EndTime).ToArray());
}
private List<BacktestingBar> CacheBarData(DataId dataId, Data data)
{
var result = new List<BacktestingBar>();
using var fileStream = System.IO.File.OpenRead(data.FilePath);
using var streamReader = new StreamReader(fileStream);
while (streamReader.ReadLine() is { } line)
{
var lineSplit = line.Split(',');
if (lineSplit.Length != 6)
{
throw new InvalidOperationException($"Invalid data format for '{dataId}'.");
}
result.Add(new BacktestingBar(DateTime.Parse(lineSplit[0]).ToUniversalTime(), double.Parse(lineSplit[1]), double.Parse(lineSplit[2]), double.Parse(lineSplit[3]), double.Parse(lineSplit[4]), long.Parse(lineSplit[5])));
}
return result;
}
private Dictionary<DateOnly, List<BacktestingTick>> CacheTickData(DataId dataId, Data data)
{
var result = new Dictionary<DateOnly, List<BacktestingTick>>();
using var fileStream = System.IO.File.OpenRead(data.FilePath);
using var streamReader = new StreamReader(fileStream);
while (streamReader.ReadLine() is { } line)
{
var lineSplit = line.Split(',');
if (lineSplit.Length != 3)
{
throw new InvalidOperationException($"Invalid data format for '{dataId}'.");
}
var time = DateTime.Parse(lineSplit[0]).ToUniversalTime();
var date = DateOnly.FromDateTime(time);
if (!result.TryGetValue(date, out var dateTicks))
{
dateTicks = new List<BacktestingTick>();
result.Add(date, dateTicks);
}
dateTicks.Add(new BacktestingTick(time, double.Parse(lineSplit[1]), double.Parse(lineSplit[2])));
}
return result;
}
private void OnRequestStatusChanged(BacktestingDataRequestStatusChangedEventArgs args)
{
Print($"Request '{args.DataRequest.PriceDataSourceId}' status changed: {args.DataRequest.Status}");
}
private BacktestingMinMaxTime OnMinMaxTimeRequested(BacktestingDataSourceMinMaxTimeArgs args)
{
Print($"Min/Max time requested, {args.PriceDataSourceId}, {args.DataType}");
var dataId = GetDataId(args.PriceDataSourceId, args.DataType);
Print($"Min/Max time request DataId: {dataId}");
if (!_addedData.TryGetValue(dataId, out var data))
throw new InvalidOperationException("No data");
Print($"Min/Max time sent, {data.MinMaxTime}");
return data.MinMaxTime;
}
private DataId GetDataId(PriceDataSourceId priceDataSourceId, BacktestingDataSourceDataType dateType)
{
return new DataId(priceDataSourceId.Symbol.Name, dateType switch
{
BacktestingDataSourceDataType.Tick => TimeFrame.Tick.Name,
BacktestingDataSourceDataType.M1OpenPrices => TimeFrame.Minute.Name,
BacktestingDataSourceDataType.OpenPrices when _customTimeFrame != null && priceDataSourceId.TimeFrame == _customTimeFrame.TimeFrame => TimeFrame.Hour.Name,
_ => priceDataSourceId.TimeFrame.Name
}, dateType);
}
private BacktestingMinMaxTime GetMinMaxTime(string filePath)
{
using var fileStream = System.IO.File.OpenRead(filePath);
using var streamReader = new StreamReader(fileStream);
string firstLine = null;
string lastLine = null;
while (streamReader.ReadLine() is { } line)
{
if (firstLine == null)
firstLine = line;
lastLine = line;
}
Print($"First Line: {firstLine}");
Print($"Last Line: {lastLine}");
return new(
!string.IsNullOrWhiteSpace(firstLine) ? DateTime.Parse(firstLine.Split(',')[0]).ToUniversalTime() : default,
!string.IsNullOrWhiteSpace(lastLine) ? DateTime.Parse(lastLine.Split(',')[0]).ToUniversalTime() : default);
}
private readonly record struct DataId(string SymbolName, string TimeFrameName, BacktestingDataSourceDataType DataType);
private readonly record struct Data(string FilePath, BacktestingMinMaxTime MinMaxTime);
}
import clr
clr.AddReference("cAlgo.API")
from cAlgo.API import *
from System import Action, Func, Array, DateTime
class CustomDataSourceCSVTest():
# Each CSV line has the format: time,open,high,low,close,volume
FilePath = r"C:\Data\EURUSD_M1.csv"
def on_start(self):
self.bars = self.read_bars(self.FilePath)
options = BacktestingDataSourceOptions(BacktestingDataSourceDataType.M1OpenPrices | BacktestingDataSourceDataType.OpenPrices, Func[BacktestingDataSourceMinMaxTimeArgs, BacktestingMinMaxTime](self.on_min_max_time_requested), Action[BacktestingDataRequest](self.on_data_requested))
self.dataSource = api.Backtesting.DataSources.Add("CSV", options)
def read_bars(self, filePath):
bars = []
with open(filePath) as file:
for line in file:
parts = line.strip().split(",")
if len(parts) != 6:
continue
bars.append(BacktestingBar(DateTime.Parse(parts[0]).ToUniversalTime(), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]), int(parts[5])))
return bars
def on_min_max_time_requested(self, args):
return BacktestingMinMaxTime(self.bars[0].Time, self.bars[-1].Time)
def on_data_requested(self, request):
requestBars = [bar for bar in self.bars if bar.Time >= request.StartTime and bar.Time <= request.EndTime]
if len(requestBars) == 0:
request.Fail(f"No CSV data between {request.StartTime} and {request.EndTime}")
return
request.Complete(BacktestingBarsData(Array[BacktestingBar](requestBars)))
{
"AccessRights": "None"
}
Methods
| Name | Description |
|---|---|
AddDesktopCLICloudBacktestingDataSource | Adds a new backtesting data source. |
RemoveDesktopCLICloudbool | Removes a previously added backtesting data source. |
RemoveAllUnusedDesktopCLICloudvoid | Removes all previously added backtesting data sources that are not being used. |