/*
* 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 System;
using System.Globalization;
using System.IO;
using NUnit.Framework;
using Python.Runtime;
using QuantConnect.Data;
using QuantConnect.Indicators;
namespace QuantConnect.Tests.Indicators
{
[TestFixture]
public class PythonIndicatorNoinheritanceSnakeCaseTests : PythonIndicatorNoinheritanceTests
{
protected override bool SnakeCase => true;
}
[TestFixture]
public class PythonIndicatorNoinheritanceTests : PythonIndicatorTests
{
protected override bool SnakeCase => false;
///
/// In this Custom Indicator, Update returns a boolean
///
protected override IndicatorBase CreateIndicator()
{
using (Py.GIL())
{
var module = PyModule.FromString(
Guid.NewGuid().ToString(),
$@"
from collections import deque
from datetime import datetime, timedelta
from numpy import sum
class CustomSimpleMovingAverage():
def __init__(self, name, period):
self.{(SnakeCase ? "name" : "Name")}= name
self.{(SnakeCase ? "value" : "Value")} = 0
self.{(SnakeCase ? "is_ready" : "IsReady")} = False
self.queue = deque(maxlen=period)
# Update method is mandatory
def {(SnakeCase ? "update" : "Update")}(self, input):
self.queue.appendleft(input.{(SnakeCase ? "value" : "Value")})
count = len(self.queue)
self.{(SnakeCase ? "value" : "Value")} = sum(self.queue) / count
self.{(SnakeCase ? "is_ready" : "IsReady")} = count == self.queue.maxlen
return self.{(SnakeCase ? "is_ready" : "IsReady")}
"
);
var indicator = module.GetAttr("CustomSimpleMovingAverage")
.Invoke("custom".ToPython(), 14.ToPython());
return new PythonIndicator(indicator);
}
}
protected override void RunTestIndicator(IndicatorBase indicator)
{
var first = true;
var closeIndex = -1;
var targetIndex = -1;
foreach (var line in File.ReadLines(Path.Combine("TestData", TestFileName)))
{
var parts = line.Split(new[] { ',' }, StringSplitOptions.None);
if (first)
{
first = false;
for (var i = 0; i < parts.Length; i++)
{
if (parts[i].Trim() == "Close")
{
closeIndex = i;
}
if (parts[i].Trim() == TestColumnName)
{
targetIndex = i;
}
}
if (closeIndex * targetIndex < 0)
{
Assert.Fail($"Didn't find one of 'Close' or '{TestColumnName}' in the header: {line}");
}
continue;
}
var close = decimal.Parse(parts[closeIndex], CultureInfo.InvariantCulture);
var date = Time.ParseDate(parts[0]);
var data = new IndicatorDataPoint(date, close);
indicator.Update(data);
if (!indicator.IsReady || string.IsNullOrEmpty(parts[targetIndex].Trim()))
{
continue;
}
var expected = double.Parse(parts[targetIndex], CultureInfo.InvariantCulture);
Assertion.Invoke(indicator, expected);
}
}
}
}