// Copyright PFSOFT LLC. © 2003-2017. All rights reserved.
using System;
using System.Collections.Generic;
using PTLRuntime.NETScript;
using System.Drawing;
using System.Linq;
using System.Text;
using PTLRuntime.NETScript.Indicators;
using System.Threading.Tasks;
using System.Threading;
using PTLRuntime.NETScript.Charts;
namespace RentTrade
{
///
/// RentTrade
///
public class RT : NETIndicator
{
#region Parameters
[InputParameter("TimeFrame", 0, new object[]
{
"1 minute", Period.Min,
"5 minutes",Period.Min5,
"15 minutes", Period.Min15,
"30 minutes", Period.Min30,
"1 hour", Period.Hour,
"4 hours", Period.Hour4,
"1 day", Period.Day,
"1 week", Period.Week,
})]
public int TimeFrame = Period.Min15;
[InputParameter("Up band price", 1, new object[]{
"Close", PriceType.Close,
"Open", PriceType.Open,
"High", PriceType.High,
"Low", PriceType.Low,
"Typical", PriceType.Typical,
"Medium", PriceType.Medium,
"Weighted", PriceType.Weighted}
)]
public PriceType UpBandPrice = PriceType.Close;
[InputParameter("Down band price", 2, new object[]{
"Close", PriceType.Close,
"Open", PriceType.Open,
"High", PriceType.High,
"Low", PriceType.Low,
"Typical", PriceType.Typical,
"Medium", PriceType.Medium,
"Weighted", PriceType.Weighted}
)]
public PriceType DownBandPrice = PriceType.Close;
[InputParameter("Period of Bollinger Bands", 3, 1, 9999)]
public int BollBandPeriod = 12;
[InputParameter("Moving average mode", 4, new object[]{
"Simple", MAMode.SMA,
"Exponential", MAMode.EMA,
"Modified", MAMode.SMMA,
"Linear Weights", MAMode.LWMA,
})]
public MAMode MaMode = MAMode.SMA;
[InputParameter("Deviation", 5, 0.1, 10.0, 1, 0.1)]
public double Deviation = 1.0;
[InputParameter("Period of Standart Deviation", 6, 1, 9999)]
public int DeviationPeriod = 7;
[InputParameter("Money risk", 7, 0.1, 1.0, 1, 0.1)]
public double MoneyRisk = 1.0;
[InputParameter("Enable alert mode", 8)]
public bool AlertMode = false;
[InputParameter("Signal mode", 9, new object[]{
"All entities", SignalMode.AllEntities,
"Display line", SignalMode.DisplayLine,
"Arrows for enter", SignalMode.Arrows,
})]
public SignalMode SignalMode = SignalMode.AllEntities;
[InputParameter("Up arrow color", 10)]
public Color UpArrowColor = Color.OrangeRed;
[InputParameter("Down arrow color", 11)]
public Color DownArrowColor = Color.DodgerBlue;
#endregion
private bool _isSelectedHistDataLoaded;
private HistoricalData _historicalData;
private Indicator _upMA;
private Indicator _dnMA;
private Indicator _upStdDev;
private Indicator _dnStdDev;
private double upBand1;
private double dnBand1;
private double dnBand2;
private double upBand2;
private int currentTrend;
private int prevTrend;
private double prevUpBand1;
private double prevDnBand1;
private double prevDnBand2;
private double prevUpBand2;
private int prevIndex;
List signals = new List();
private Pen penBearish;
private Pen penBullish;
private bool _ChartDrawn;
private Dictionary _periodDict;
public RT()
: base()
{
#region Initialization
base.Author = "";
base.Comments = "";
base.Company = "";
base.Copyrights = "";
base.DateOfCreation = "14.03.2017";
base.ExpirationDate = 0;
base.Version = "";
base.Password = "66b4a6416f59370e942d353f08a9ae36";
base.ProjectName = "RentTrade";
#endregion
base.SetIndicatorLine("Trend line", Color.Orange, 2, LineStyle.SimpleChart);
base.SeparateWindow = false;
_periodDict = new Dictionary
{
{ Period.Min,"1 minute" },
{ Period.Min5 , "5 minutes" },
{ Period.Min15, "15 minutes" },
{ Period.Min30, "30 minutes" },
{ Period.Hour,"1 hour" },
{ Period.Hour4,"4 hours" },
{ Period.Day, "1 day" },
{ Period.Week, "1 week" },
};
}
///
/// This function will be called after creating
///
public override void Init()
{
if (TimeFrame < CurrentData.Period)
TimeFrame = CurrentData.Period;
base.IndicatorShortName(string.Format("{0} ({1}; {2})", ProjectName, BollBandPeriod, _periodDict[TimeFrame]));
penBearish = new Pen(DownArrowColor);
penBearish.StartCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
penBullish = new Pen(UpArrowColor);
penBullish.StartCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
signals.Insert(0, new Signal());
_historicalData = GetHistoricalData(new HistoricalDataRequest(Instruments.Current, TimeFrame, CurrentData.DataType));
_isSelectedHistDataLoaded = false;
}
///
/// Entry point. This function is called when new quote comes
///
public override void OnQuote()
{
if (_isSelectedHistDataLoaded)
{
// Calculate a position into new historicalData
if (_historicalData.FindInterval(CurrentData.Time()) < 0)
return;
var hdIndex = _historicalData.Count - _historicalData.FindInterval(CurrentData.Time()) - 1;
var currIndex = CurrentData.FindInterval(_historicalData.Time(hdIndex));
var counter = currIndex >= 0 ? CurrentData.Count - currIndex : 1;
if (currIndex > prevIndex)
{
MTFNextBar(); // Invoke MTFNextBar of current timeframe
prevIndex = currIndex;
}
var result = 0d;
var signal = new Signal();
upBand1 = _upMA.GetValue(0, hdIndex) + Deviation * _upStdDev.GetValue(0, hdIndex);
dnBand1 = _dnMA.GetValue(0, hdIndex) - Deviation * _dnStdDev.GetValue(0, hdIndex);
upBand2 = upBand1 + 0.5 * (MoneyRisk - 1) * (upBand1 - dnBand1);
dnBand2 = dnBand1 - 0.5 * (MoneyRisk - 1) * (upBand1 - dnBand1);
if (_historicalData.GetPrice(PriceType.Close, hdIndex) > prevUpBand1)
currentTrend = 1;
if (_historicalData.GetPrice(PriceType.Close, hdIndex) < prevDnBand1)
currentTrend = -1;
if (currentTrend > 0)
{
if (dnBand1 < prevDnBand1)
dnBand1 = prevDnBand1;
if (dnBand2 < prevDnBand2)
dnBand2 = prevDnBand2;
result = dnBand2;
if (prevTrend < 0)
{
var value = Math.Min(dnBand2, _historicalData.GetPrice(PriceType.Low, hdIndex));
signal = new Signal(_historicalData.Time(hdIndex), OperationType.Buy, value);
}
}
if (currentTrend < 0)
{
if (upBand1 > prevUpBand1)
upBand1 = prevUpBand1;
if (upBand2 > prevUpBand2)
upBand2 = prevUpBand2;
result = upBand2;
if (prevTrend > 0)
{
var value = Math.Max(upBand2, _historicalData.GetPrice(PriceType.High, hdIndex));
signal = new Signal(_historicalData.Time(hdIndex), OperationType.Sell, value);
}
}
// Display Alert
if (_ChartDrawn && signal.Operation != OperationType.None && signals[0].Operation == OperationType.None && AlertMode)
Alert(string.Format("{0} signal. StopLoss value is {1}.", signal.Operation, result));
signals[0] = signal; // Rewrite the active (zero index) signal object
// Redraw "whole" bar (on big timeframes)
if (SignalMode != SignalMode.Arrows)
{
for (int i = 0; i < counter; i++)
SetValue(0, i, result);
}
}
}
private void MTFNextBar()
{
prevDnBand1 = dnBand1;
prevDnBand2 = dnBand2;
prevUpBand1 = upBand1;
prevUpBand2 = upBand2;
prevTrend = currentTrend;
if (signals[0].Operation != OperationType.None)
signals.Insert(0, new Signal()); // Paste the empty signal for rewriting in the OnQuote()
}
public override void NextBar()
{
// Load a new historicaData
if (!_isSelectedHistDataLoaded)
{
var task = Task.Run(() => Initialize());
_isSelectedHistDataLoaded = task.Result;
}
_ChartDrawn = IsHistoryLoaded();
}
///
/// This function will be called before removing
///
public override void Complete()
{
_dnStdDev.Dispose();
_upStdDev.Dispose();
_upMA.Dispose();
_dnMA.Dispose();
_historicalData = null;
_dnMA = null;
_dnStdDev = null;
_upStdDev = null;
_upMA = null;
}
public override void OnPaintChart(object sender, PaintChartEventArgs args)
{
if (SignalMode != SignalMode.DisplayLine)
{
args.Graphics.SetClip(args.Rectangle);
var barWidth = CurrentChart.GetChartPoint(CurrentData.Time(), 0).X - CurrentChart.GetChartPoint(CurrentData.Time(TimeFrame / CurrentData.Period), 0).X;
var selectedSignals = signals.Where(s => s.BarTime >= CurrentChart.GetTimePrice(0, 0).Time && s.BarTime <= CurrentChart.GetTimePrice(args.Rectangle.Width, 0).Time);
foreach (Signal signal in selectedSignals)
{
var point = CurrentChart.GetChartPoint(signal.BarTime, signal.Value);
switch (signal.Operation)
{
case OperationType.Buy:
penBearish.Color = DownArrowColor;
penBearish.Width = barWidth / 2;
args.Graphics.DrawLine(penBearish, new PointF(point.X + barWidth / 2, point.Y + barWidth), new PointF(point.X + barWidth / 2, point.Y + barWidth * 2));
break;
case OperationType.Sell:
penBullish.Color = UpArrowColor;
penBullish.Width = barWidth / 2;
args.Graphics.DrawLine(penBullish, new PointF(point.X + barWidth / 2, point.Y - barWidth), new PointF(point.X + barWidth / 2, point.Y - barWidth * 2));
break;
}
}
}
base.OnPaintChart(sender, args);
}
#region Misc
private bool IsHistoryLoaded()
{
var dt = DateTime.UtcNow;
var totalSecondsOfCurrentDateTime = dt.TimeOfDay.TotalSeconds;
var BarTimeSeconds = CurrentData.Time().TimeOfDay.TotalSeconds;
var i = Period.Second == CurrentData.Period ? 0 : 60;
return CurrentData.Time().Date == dt.Date && BarTimeSeconds + CurrentData.Period * i > totalSecondsOfCurrentDateTime;
}
private bool Initialize()
{
var date = CurrentData.Time(CurrentData.Count - 1);
_upMA = Indicators.iMA(_historicalData, BollBandPeriod, MaMode, 0, UpBandPrice);
_dnMA = Indicators.iMA(_historicalData, BollBandPeriod, MaMode, 0, DownBandPrice);
_upStdDev = Indicators.iStdDev(_historicalData, DeviationPeriod, MaMode, UpBandPrice);
_dnStdDev = Indicators.iStdDev(_historicalData, DeviationPeriod, MaMode, UpBandPrice);
_historicalData.Load(date);
return true;
}
#endregion
}
public enum SignalMode
{
AllEntities,
DisplayLine,
Arrows
}
enum OperationType
{
None,
Buy,
Sell
}
class Signal
{
public DateTime BarTime { get; private set; }
public OperationType Operation { get; private set; }
public double Value { get; private set; }
public Signal(DateTime time, OperationType operation, double value)
{
BarTime = time;
Operation = operation;
Value = value;
}
public Signal()
{
BarTime = new DateTime();
Operation = OperationType.None;
Value = 0;
}
}
}