Skip to content

Commit

Permalink
Add custom ToString() method in API classes (#8357)
Browse files Browse the repository at this point in the history
* Potential solution

* Improve solution

* Nit change

* Nit change
  • Loading branch information
Marinovsky authored Oct 7, 2024
1 parent 0a2c05a commit 3304814
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Common/Api/Estimate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace QuantConnect.Api
/// <summary>
/// Estimate response packet from the QuantConnect.com API.
/// </summary>
public class Estimate
public class Estimate: StringRepresentation
{
/// <summary>
/// Estimate id
Expand Down
2 changes: 1 addition & 1 deletion Common/Api/Organization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class OrganizationResponse : RestResponse
/// <summary>
/// Object representation of Organization from QuantConnect Api
/// </summary>
public class Organization
public class Organization: StringRepresentation
{
/// <summary>
/// Data Agreement information
Expand Down
2 changes: 1 addition & 1 deletion Common/Api/RestResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace QuantConnect.Api
/// <summary>
/// Base API response class for the QuantConnect API.
/// </summary>
public class RestResponse
public class RestResponse: StringRepresentation
{
/// <summary>
/// JSON Constructor
Expand Down
33 changes: 33 additions & 0 deletions Common/Api/StringRepresentation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using Newtonsoft.Json;

namespace QuantConnect.Api
{
/// <summary>
/// Class to return the string representation of an API response class
/// </summary>
public class StringRepresentation
{
/// <summary>
/// Returns the string representation of this object
/// </summary>
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}
2 changes: 1 addition & 1 deletion Common/Orders/OrdersResponseWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class OrdersResponseWrapper : RestResponse
/// Api order and order events reponse
/// </summary>
[JsonConverter(typeof(ReadOrdersResponseJsonConverter))]
public class ApiOrderResponse
public class ApiOrderResponse: StringRepresentation
{
/// <summary>
/// The symbol associated with this order
Expand Down
17 changes: 17 additions & 0 deletions Tests/Api/ApiTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using System;
using System.Collections;
using System.IO;
using System.Text.Json;
using System.Threading;

namespace QuantConnect.Tests.API
Expand Down Expand Up @@ -128,6 +129,22 @@ private void DeleteTestProjectAndBacktest()
}
}

public static bool IsValidJson(string jsonString)
{
try
{
using (JsonDocument doc = JsonDocument.Parse(jsonString))
{

}
return true;
}
catch (JsonException)
{
return false;
}
}

/// <summary>
/// Wait for the compiler to respond to a specified compile request
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions Tests/Api/DataTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public void ValidForexDataLinks(string ticker, string market, DateTime date, Res
new Symbol(SecurityIdentifier.GenerateForex(ticker, market), ticker),
date, resolution, tickType);
var dataLink = ApiClient.ReadDataLink(path, TestOrganization);
var stringRepresentation = dataLink.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));

Assert.IsTrue(dataLink.Success);
Assert.IsFalse(dataLink.Link.IsNullOrEmpty());
Expand All @@ -92,6 +94,8 @@ public void GetPrices(string filePath)
if (_pricesCache == null)
{
_pricesCache = ApiClient.ReadDataPrices(TestOrganization);
var stringRepresentation = _pricesCache.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
}

// Make sure we actually have these prices for the test to work
Expand Down Expand Up @@ -141,6 +145,8 @@ public void DataPriceRegex(string dataFile, string matchingRegex)
public void GetDataListings(string directory)
{
var dataList = ApiClient.ReadDataDirectory(directory);
var stringRepresentation = dataList.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(dataList.Success);
Assert.IsTrue(dataList.AvailableData.Count > 0);
}
Expand Down
4 changes: 4 additions & 0 deletions Tests/Api/LiveTradingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,8 @@ public void LiveAlgorithmsAndLiveLogs_CanBeRead_Successfully()
{
// Read all currently running algorithms
var liveAlgorithms = ApiClient.ListLiveAlgorithms(AlgorithmStatus.Running);
var stringRepresentation = liveAlgorithms.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));

Assert.IsTrue(liveAlgorithms.Success);
// There has to be at least one running algorithm
Expand All @@ -702,6 +704,8 @@ public void LiveAlgorithmsAndLiveLogs_CanBeRead_Successfully()
// Read the logs of the first live algorithm
var firstLiveAlgo = liveAlgorithms.Algorithms[0];
var liveLogs = ApiClient.ReadLiveLogs(firstLiveAlgo.ProjectId, firstLiveAlgo.DeployId, 0, 20);
stringRepresentation = liveLogs.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));

Assert.IsTrue(liveLogs.Success);
Assert.IsTrue(liveLogs.Logs.Any());
Expand Down
4 changes: 4 additions & 0 deletions Tests/Api/ObjectStoreTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public void GetObjectStoreWorksAsExpected(List<string> keys, bool isSuccessExpec
public void GetObjectStorePropertiesWorksAsExpected(string key, bool isSuccessExpected)
{
var result = ApiClient.GetObjectStoreProperties(TestOrganization, key);
var stringRepresentation = result.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
if (isSuccessExpected)
{
Assert.IsTrue(result.Success);
Expand Down Expand Up @@ -84,6 +86,8 @@ public void DeleteObjectStoreWorksAsExpected()
var result = ApiClient.SetObjectStore(TestOrganization, _key, _data);
Assert.IsTrue(result.Success);
var objectsBefore = ApiClient.ListObjectStore(TestOrganization, _key);
var stringRepresentation = objectsBefore.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));

result = ApiClient.DeleteObjectStore(TestOrganization, _key);
Assert.IsTrue(result.Success);
Expand Down
2 changes: 2 additions & 0 deletions Tests/Api/OptimizationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ public void EstimateOptimization()
new Constraint("TotalPerformance.PortfolioStatistics.SharpeRatio", ComparisonOperatorTypes.GreaterOrEqual, 1)
}
);
var stringRepresentation = estimate.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));

Assert.IsNotNull(estimate);
Assert.IsNotEmpty(estimate.EstimateId);
Expand Down
4 changes: 4 additions & 0 deletions Tests/Api/OrganizationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public class OrganizationTests : ApiTestBase
public void ReadAccount()
{
var account = ApiClient.ReadAccount();
var stringRepresentation = account.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));

Assert.IsTrue(account.Success);
Assert.IsNotEmpty(account.OrganizationId);
Expand All @@ -41,6 +43,8 @@ public void ReadAccount()
public void ReadOrganization()
{
var organization = ApiClient.ReadOrganization(TestOrganization);
var stringRepresentation = organization.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));

Assert.AreNotEqual(default(DateTime), organization.DataAgreement.Signed);
Assert.AreNotEqual(0, organization.DataAgreement.EpochSignedTime);
Expand Down
38 changes: 38 additions & 0 deletions Tests/Api/ProjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public void Projects_CanBeCreatedAndDeleted_Successfully()

//Test create a new project successfully
var project = ApiClient.CreateProject(name, Language.CSharp, TestOrganization);
var stringRepresentation = project.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(project.Success);
Assert.Greater(project.Projects.First().ProjectId, 0);
Assert.AreEqual(name, project.Projects.First().Name);
Expand Down Expand Up @@ -122,6 +124,8 @@ public void CRUD_ProjectFiles_Successfully()

// Add random file
var randomAdd = ApiClient.AddProjectFile(TestProject.ProjectId, fakeFile.Name, fakeFile.Code);
var stringRepresentation = randomAdd.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(randomAdd.Success);
// Update names of file
var updatedName = ApiClient.UpdateProjectFileName(TestProject.ProjectId, fakeFile.Name, realFile.Name);
Expand All @@ -133,6 +137,8 @@ public void CRUD_ProjectFiles_Successfully()

// Read single file
var readFile = ApiClient.ReadProjectFile(TestProject.ProjectId, realFile.Name);
stringRepresentation = readFile.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(readFile.Success);
Assert.IsTrue(readFile.Files.First().Code == realFile.Code);
Assert.IsTrue(readFile.Files.First().Name == realFile.Name);
Expand Down Expand Up @@ -165,6 +171,8 @@ public void RU_ProjectNodes_Successfully()
{
// Read the nodes
var nodesResponse = ApiClient.ReadProjectNodes(TestProject.ProjectId);
var stringRepresentation = nodesResponse.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(nodesResponse.Success);
Assert.Greater(nodesResponse.Nodes.BacktestNodes.Count, 0);

Expand Down Expand Up @@ -349,10 +357,18 @@ public void ReadBacktestOrdersReportAndChart()
// Now wait until the backtest is completed and request the orders again
backtestRead = WaitForBacktestCompletion(project.ProjectId, backtest.BacktestId);
var backtestOrdersRead = ApiClient.ReadBacktestOrders(project.ProjectId, backtest.BacktestId);
string stringRepresentation;
foreach(var backtestOrder in backtestOrdersRead)
{
stringRepresentation = backtestOrder.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
}
Assert.IsTrue(backtestOrdersRead.Any());
Assert.AreEqual(Symbols.SPY.Value, backtestOrdersRead.First().Symbol.Value);

var readBacktestReport = ApiClient.ReadBacktestReport(project.ProjectId, backtest.BacktestId);
stringRepresentation = readBacktestReport.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(readBacktestReport.Success);
Assert.IsFalse(string.IsNullOrEmpty(readBacktestReport.Report));

Expand All @@ -362,6 +378,8 @@ public void ReadBacktestOrdersReportAndChart()
new DateTime(2013, 10, 11).Second,
1000,
backtest.BacktestId);
stringRepresentation = readBacktestChart.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(readBacktestChart.Success);
Assert.IsNotNull(readBacktestChart.Chart);

Expand Down Expand Up @@ -474,6 +492,8 @@ public void UpdatesBacktestTags()

// Read the backtest and verify the tags were added
var backtestsResult = ApiClient.ListBacktests(TestProject.ProjectId);
var stringRepresentation = backtestsResult.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(backtestsResult.Success, $"Error getting backtests:\n {string.Join("\n ", backtestsResult.Errors)}");
Assert.AreEqual(1, backtestsResult.Backtests.Count);
CollectionAssert.AreEquivalent(tags, backtestsResult.Backtests[0].Tags);
Expand All @@ -498,9 +518,13 @@ public void ReadBacktestInsightsWorksAsExpected()
// Create backtest
var backtestName = $"ReadBacktestOrders Backtest {GetTimestamp()}";
var backtest = ApiClient.CreateBacktest(projectId, compileId, backtestName);
var stringRepresentation = backtest.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));

// Try to read the insights from the algorithm
var readInsights = ApiClient.ReadBacktestInsights(projectId, backtest.BacktestId, 0, 5);
stringRepresentation = readInsights.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
var finish = DateTime.UtcNow.AddMinutes(2);
do
{
Expand Down Expand Up @@ -556,6 +580,8 @@ public void CreatesLiveAlgorithm()

// Create compile
var compile = ApiClient.CreateCompile(projectId);
var stringRepresentation = compile.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(compile.Success);

// Wait at max 30 seconds for project to compile
Expand All @@ -573,10 +599,14 @@ public void CreatesLiveAlgorithm()
{
// Create live default algorithm
var createLiveAlgorithm = ApiClient.CreateLiveAlgorithm(projectId, compile.CompileId, freeNode.FirstOrDefault().Id, _defaultSettings, dataProviders: dataProviders);
stringRepresentation = createLiveAlgorithm.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(createLiveAlgorithm.Success, $"ApiClient.CreateLiveAlgorithm(): Error: {string.Join(",", createLiveAlgorithm.Errors)}");

// Read live algorithm
var readLiveAlgorithm = ApiClient.ReadLiveAlgorithm(projectId, createLiveAlgorithm.DeployId);
stringRepresentation = readLiveAlgorithm.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(readLiveAlgorithm.Success, $"ApiClient.ReadLiveAlgorithm(): Error: {string.Join(",", readLiveAlgorithm.Errors)}");

// Stop the algorithm
Expand All @@ -588,6 +618,8 @@ public void CreatesLiveAlgorithm()
Assert.IsNotNull(readChart.Chart);

var readLivePortfolio = ApiClient.ReadLivePortfolio(projectId);
stringRepresentation = readLivePortfolio.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(readLivePortfolio.Success, $"ApiClient.ReadLivePortfolio(): Error: {string.Join(",", readLivePortfolio.Errors)}");
Assert.IsNotNull(readLivePortfolio.Portfolio, "Portfolio was null!");
Assert.IsNotNull(readLivePortfolio.Portfolio.Cash, "Portfolio.Cash was null!");
Expand Down Expand Up @@ -615,6 +647,8 @@ public void CreatesLiveAlgorithm()
public void ReadVersionsWorksAsExpected()
{
var result = ApiClient.ReadLeanVersions();
var stringRepresentation = result.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));
Assert.IsTrue(result.Success);
Assert.IsNotEmpty(result.Versions);
}
Expand Down Expand Up @@ -673,6 +707,8 @@ public void CreatesOptimization()
nodeType: OptimizationNodes.O2_8,
parallelNodes: 12
);
var stringRepresentation = optimization.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));

var finish = DateTime.UtcNow.AddMinutes(5);
var readOptimization = ApiClient.ReadOptimization(optimization.OptimizationId);
Expand All @@ -682,6 +718,8 @@ public void CreatesOptimization()
readOptimization = ApiClient.ReadOptimization(optimization.OptimizationId);
}
while (finish > DateTime.UtcNow && readOptimization.Status != OptimizationStatus.Completed);
stringRepresentation = readOptimization.ToString();
Assert.IsTrue(ApiTestBase.IsValidJson(stringRepresentation));

Assert.IsNotNull(optimization);
Assert.IsNotEmpty(optimization.OptimizationId);
Expand Down

0 comments on commit 3304814

Please sign in to comment.