| Overall Statistics |
|
Total Trades 1143 Average Win 3.60% Average Loss -3.65% Compounding Annual Return -6.237% Drawdown 98.200% Expectancy 0.042 Net Profit -36.316% Sharpe Ratio 0.464 Loss Rate 48% Win Rate 52% Profit-Loss Ratio 0.99 Alpha -0.576 Beta 51.005 Annual Standard Deviation 0.96 Annual Variance 0.922 Information Ratio 0.443 Tracking Error 0.96 Treynor Ratio 0.009 Total Fees $1419.49 |
/*
Wenbo Zhnag 9/17/17 on https://www.quantopian.com/posts/ballistic-xiv-slash-vxx-my-best-long-xiv-slash-vxx-strategy-to-date
Also read https://www.quantopian.com/posts/ballistic-xiv-slash-vxx-my-best-long-xiv-slash-vxx-strategy-to-date
*/
namespace QuantConnect
{
public partial class TestConsolidatedIndicator : QCAlgorithm
{
//Editable parameters
int context_xivTradePercent = 100;
int context_tltTradePercent = 100;
int context_vxxTradePercent = 100;
decimal context_StopLossPct = 0.25M;
//Used for logging
decimal context_StopPrice = 0;
decimal context_BuyPrice = 0;
decimal context_SellLossPrice = 0;
decimal context_SellProfitPrice = 0;
bool context_last_bar = false;
bool context_sell = false;
public bool context_buy = false;
bool context_buyVXX = false;
// FR#2: Delayed buy;
// context.buyTLT = False;
// FR#4: Improve buy/sell condition
decimal context_rsi_last = -1;
decimal context_newsl = 0;
decimal rsi_current = 0 ;
decimal rsi_previous = 0;
public void BarDetails()
{
// return String.Format("{0} - {1}", pBar.Time, pBar.Price);
if (Data["XIV"].Bars.Count > 0)
{
var bar = Data["XIV"].Bars[0];
LogIt(String.Format("{0} - {1}", bar.Time, bar.Price));
}
if (Data["XIV"].Bars.Count > 1)
{
var bar = Data["XIV"].Bars[1];
LogIt(String.Format("{0} - {1}", bar.Time, bar.Price));
}
//return ;
}
public void bar_open()
{
var vMethodName = "bar_open";
LogMethodStart(vMethodName);
BarDetails();
//return;
if ((context_buy == true) && (Transactions.GetOpenOrders().Count == 0))
{
set_fixed_stop();
// TODO: Turm the below two If statements into a loop from dictionary, and the list is where symbol in PositionType.Overnight, PositionType.Base
// Also verify that after Liquidate the there are no more positions held.
// #FR#2: Delayed buy
if (Portfolio[Symbols[PositionType.Overnight]].Quantity > 0)
{
Liquidate(Symbols[PositionType.Overnight]);
LogIt("Sell " + Symbols[PositionType.Overnight]);
}
if (Portfolio[Symbols[PositionType.Base]].Quantity > 0)
{
Liquidate(Symbols[PositionType.Base]);
LogIt("Sell " + Symbols[PositionType.Base] + " at " + Price(Symbols[PositionType.Base]));
}
// Fill a market order immediately (before moving to next line of code)
var newTicket = MarketOrder(Symbols[PositionType.Inverse], context_xivTradePercent);
LogIt("Buy XIV at " + context_BuyPrice);
context_buy = false;
context_sell = false;
context_buyVXX = false;
context_newsl = 0;
}
if ( (context_buyVXX == true) && (Portfolio[Symbols[PositionType.Base]].Quantity == 0) && (Transactions.GetOpenOrders().Count == 0) )
{
var newTicket = MarketOrder(Symbols[PositionType.Base], context_vxxTradePercent);
LogIt("Buy VXX at " + Price(Symbols[PositionType.Base]));
context_buyVXX = false;
}
if ((context_sell == true) && (Portfolio[Symbols[PositionType.Inverse]].Quantity > 0))
{
Liquidate(Symbols[PositionType.Inverse]);
LogIt("Sell " + Symbols[PositionType.Inverse] + " at " + Price(Symbols[PositionType.Inverse]));
context_sell = false;
context_buy = false;
}
LogMethodEnd(vMethodName);
}
public void my_rebalance()
{
var vMethodName = "my_rebalance";
LogMethodStart(vMethodName);
// Get RSI for every 2 hour period for the last 10 periods.
rsi_current = Data[Symbols[PositionType.Inverse]].RSI_History[0].Value;
rsi_previous = rsi_current;
if (Data[Symbols[PositionType.Inverse]].RSI_History.Count > 1)
{
rsi_previous = Data[Symbols[PositionType.Inverse]].RSI_History[1].Value;
if (context_rsi_last == -1)
{
context_rsi_last = Data[Symbols[PositionType.Inverse]].RSI_History[1].Value;
}
}
var vxx_price = Price(Symbols[PositionType.Base]);
var xiv_price = Price(Symbols[PositionType.Inverse]);
var message = String.Format("Current XIV price {0:0.00}, VXX price {1:0.00} @ RSI_2 level {2}, previous {3}, last stored {4}"
, xiv_price, vxx_price, rsi_current, rsi_previous, context_rsi_last);
LogIt(message);
//BUY RULE
if ((context_buy == false) && (context_rsi_last < 70) && (rsi_current >= 70) && (Portfolio[Symbols[PositionType.Base]].Quantity == 0))
{
context_buy = true;
}
//SELL RULE
if ((context_rsi_last > 85) && (rsi_current <= 85) && (HaveNoHoldingsIn(PositionType.Inverse)) && IsTransactionDone())
{
Liquidate(Symbols[PositionType.Inverse]);
LogIt(String.Format("Sell {0} at {1:0.00}", Symbols[PositionType.Inverse],xiv_price) );
context_buy = false;
}
if ((context_buyVXX == false) && (rsi_current <= 30) && (HaveNoHoldingsIn(PositionType.Base)))
{
context_buyVXX = true;
}
//Buy VXX rule
if ((context_buyVXX == false) && (context_rsi_last > 30) && (rsi_current <= 30)
&& (HaveNoHoldingsIn(PositionType.Base)) && (HaveNoHoldingsIn(PositionType.Inverse)) )
{
context_buyVXX = true;
}
// Sell VXX rule
if ((context_rsi_last < 15) && (rsi_current >= 15) && ( !HaveNoHoldingsIn(PositionType.Base)))
{
Liquidate(Symbols[PositionType.Base]);
LogIt(String.Format("Sell {0} at {1:0.00}", Symbols[PositionType.Base], vxx_price) );
context_buyVXX = false;
}
// panic
// if (Time.Hour > 12)
{
var high = -1M;
var long_history = History(Symbols[PositionType.Inverse], 119, Resolution.Minute );
// var long_history = History(Symbols[PositionType.Inverse], TimeSpan.FromMinutes(119), Resolution.Minute );
// LogIt(long_history.Count());
if (long_history.Count() > 0)
{
high = long_history.Max(element => (element.High)); // 10M ;
}
/*
TODO: Note this is the current implementation of context.last_bar. Will need to understand more before I figure this out properly
*/
if (Time.Hour >= 16)
{
high = History(Symbols[PositionType.Inverse], 29, Resolution.Minute).Max(element => (element.High));
}
// TODO: I think the logic is, if current price is less than 10% of high --Check and confirm
if (((high/xiv_price) - 1) > (decimal).1)
{
Liquidate(Symbols[PositionType.Inverse]);
LogIt(String.Format("Panic Sell {0} at {1:0.00}", Symbols[PositionType.Inverse], xiv_price) );
context_sell = false;
context_buy = false;
}
}
context_rsi_last = rsi_current;
LogMethodEnd(vMethodName);
}
public void set_fixed_stop()
{
var vMethodName = "set_fixed_stop";
LogMethodStart(vMethodName);
// Only call this once when the stock is bought
var price = Price(Symbols[PositionType.Inverse]);
// data.current(Symbols[PositionType.Inverse], 'price'); // Quantopian code
context_BuyPrice = price;
context_SellLossPrice = Math.Max(context_StopPrice, price - (context_StopLossPct * price));
LogMethodEnd(vMethodName);
}
public void handle_data()
{
var vxx_price = Price(Symbols[PositionType.Base]);
var xiv_price = Price(Symbols[PositionType.Inverse]);
if (context_BuyPrice > 0)
{
if ((HaveHoldingsIn(PositionType.Inverse)) )
{
// set break even
if (((xiv_price / context_BuyPrice) >= (decimal)1.1) && ( context_BuyPrice > context_SellLossPrice))
{
context_SellLossPrice = context_BuyPrice;
LogIt(String.Format("New {0} stop loss price {1:0.00}", Symbols[PositionType.Inverse], context_SellLossPrice));
}
}
// 50% trailing stop
// FR#1: Add profit take 50%
if (((xiv_price / context_BuyPrice) >= (decimal)1.5) && ( context_BuyPrice > context_SellLossPrice))
{
context_SellLossPrice = xiv_price;
LogIt(String.Format("{0} take HUGE profit at {1:0.00}", Symbols[PositionType.Inverse], context_SellLossPrice));
}
}
// If we have a position check sell conditions
if ((xiv_price <= context_SellLossPrice) && IsTransactionDone())
{
Liquidate(Symbols[PositionType.Inverse]);
LogIt(String.Format("Stop loss sell {0} at {1:0.00}", Symbols[PositionType.Inverse], xiv_price));
context_sell = false;
context_buy = false;
}
}
// TODO: Link OnData to Handle_Data and then testS
}
}namespace QuantConnect
{
/// <summary>
/// Cloned from https://github.com/QuantConnect/Lean/blob/master/Algorithm.CSharp/MultipleSymbolConsolidationAlgorithm.cs
/// </summary>
/* Things to do:
1. Add two or more symbols to be looked at.
2. Get the last 10 prices for each 2 hour period.
This could be over multiple days, ignore weekend and holidays.
3. Warmup does not seem to be working
4. In the documentation I saw a line for RegisterIndicator. Is this not required?
5. How would I used Consolidators with long and short SMA.
6. How would I used Consolidators withe multiple indicators - SMA, RSI - Long and short for each
7. The "Bar" has two values Time and EndTime. The data provided is for which time?
8. Could the SMA be defined with
symbolData.SMA = SMA(symbolData.Symbol, SimpleMovingAveragePeriod, ResolutionPeriod);
9.
*/
/* Errors:
*/
public partial class TestConsolidatedIndicator : QCAlgorithm
{
public struct PositionType
{
private PositionType(string value) { Value = value; }
public string Value { get; set; }
public static PositionType Base { get { return new PositionType("Base"); } }
public static PositionType Inverse { get { return new PositionType("Inverse"); } }
public static PositionType Overnight { get { return new PositionType("Overnight"); } }
}
private const int BarPeriodValue = 1;
private const int MINUTES_IN_HOUR = 60;
private const string SPY = "SPY";
/// <summary>
/// This is the period of bars we'll be creating
/// </summary>
public readonly Resolution ResolutionPeriod = Resolution.Minute;
/// <summary>
/// This is the period of our sma SLOW indicators
/// </summary>
public readonly int SimpleMovingAveragePeriod_Small = 2;
/// <summary>
/// This is the period of our sma FAST indicators
/// </summary>
public readonly int SimpleMovingAveragePeriod_Large = 5;
/// <summary>
/// This is the number of consolidated bars we'll hold in symbol data for reference
/// </summary>
public readonly int RollingWindowSize = 5;
/// <summary>
/// Holds all of our data keyed by each symbol
/// </summary>
public readonly Dictionary<string, ConsolidatedIndicator> Data = new Dictionary<string, ConsolidatedIndicator>();
/// <summary>
/// Contains all of our equity symbols
/// </summary>
public readonly Dictionary<PositionType, string> Symbols = new Dictionary<PositionType, string> {
{PositionType.Base, "VXX" },
{PositionType.Inverse, "XIV" },
{PositionType.Overnight, "TLT" }
};
/// <summary>
/// This is the period of bars we'll be creating
/// </summary>
public TimeSpan GetBarPeriod()
{
switch (ResolutionPeriod)
{
case Resolution.Minute:
return TimeSpan.FromMinutes(BarPeriodValue);
case Resolution.Hour:
return TimeSpan.FromHours(BarPeriodValue);
case Resolution.Daily:
return TimeSpan.FromDays(BarPeriodValue);
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
/// </summary>
public override void Initialize()
{
SetStartDate(2011, 01, 5); // Set Start Date
SetEndDate(2018, 1, 5); // Set End Date
SetCash(100000); // Set Strategy Cash
//SetWarmUp(RollingWindowSize + 2); // Happens after Initalize is done.
AddEquity(SPY, ResolutionPeriod);
foreach(var entry in Symbols)
{
var symbol = entry.Value;
var equity = AddEquity(symbol, ResolutionPeriod);
Debug(symbol);
Data.Add(symbol, new ConsolidatedIndicator(equity.Symbol, GetBarPeriod(), RollingWindowSize));
}
ScheduleMethods();
// loop through all our symbols and request data subscriptions and initialize indicators
foreach (var kvp in Data)
{
// this is required since we're using closures below, for more information
// see: http://stackoverflow.com/questions/14907987/access-to-foreach-variable-in-closure-warning
var symbolData = kvp.Value;
// define a consolidator to consolidate data for this symbol on the requested period
var consolidator = symbolData.Symbol.SecurityType == SecurityType.Equity
? (IDataConsolidator)new TradeBarConsolidator(GetBarPeriod())
: (IDataConsolidator)new QuoteBarConsolidator(GetBarPeriod());
// define our indicator
// symbolData.SMA_Small = new SimpleMovingAverage(CreateIndicatorName(symbolData.Symbol, "SMA" + SimpleMovingAveragePeriod_Small, ResolutionPeriod), SimpleMovingAveragePeriod_Small);
// symbolData.SMA_Large = new SimpleMovingAverage(CreateIndicatorName(symbolData.Symbol, "SMA" + SimpleMovingAveragePeriod_Large, ResolutionPeriod), SimpleMovingAveragePeriod_Large);
symbolData.RSI = new RelativeStrengthIndex(CreateIndicatorName(symbolData.Symbol, "RSI" + SimpleMovingAveragePeriod_Small, ResolutionPeriod), SimpleMovingAveragePeriod_Small, MovingAverageType.Simple);
// wire up our consolidator to update the indicator
consolidator.DataConsolidated += (sender, baseData) =>
{
IReadOnlyList<string> TimesToCheck = new List<string>{ "9:39", "11:29", "1:29", "3:29", "4:00" };
if (!TimesToCheck.Contains(Time.ToString("h:mm")))
{
return;
}
// 'bar' here is our newly consolidated data
var bar = (IBaseDataBar)baseData;
// LogIt(Time.ToString("u") + " " + bar);
// we're also going to add this bar to our rolling window so we have access to it later
symbolData.Bars.Add(bar);
// update the indicator
// symbolData.SMA_Small.Update(bar.Time, bar.Close);
// symbolData.SMA_Large.Update(bar.Time, bar.Close);
symbolData.RSI.Update(bar.Time, bar.Close);
symbolData.RSI_History.Add(symbolData.RSI.Current);
};
// consolidator.DataConsolidated += HandleConsolidatedData;
// we need to add this consolidator so it gets auto updates
SubscriptionManager.AddConsolidator(symbolData.Symbol, consolidator);
}
// Debug("Is the warmup done?");
}
private void ScheduleMethods()
{
IReadOnlyList<int> MyRebalanceHours = new List<int>{ 2, 4, 6 };
foreach (var hour in MyRebalanceHours)
{
// LogIt(hour);
Schedule.On(DateRules.EveryDay(SPY), TimeRules.AfterMarketOpen(SPY, (MINUTES_IN_HOUR * hour) + 2), my_rebalance);
}
Schedule.On(DateRules.EveryDay(SPY), TimeRules.AfterMarketOpen(SPY, 11), my_rebalance);
Schedule.On(DateRules.EveryDay(SPY), TimeRules.BeforeMarketClose(SPY, 25), my_rebalance);
IReadOnlyList<int> BarOpenHours = new List<int>{0, 2, 4, 6 };
foreach (var hour in BarOpenHours)
{
Schedule.On(DateRules.EveryDay(SPY), TimeRules.AfterMarketOpen(SPY, (MINUTES_IN_HOUR * hour) + 5), bar_open);
}
}
public void scheduled_test()
{
Debug(Time.Hour + ":" + Time.Minute);
}
public void HandleConsolidatedData(object pSender, IBaseData pBaseData)
{
//if (IsWarmingUp) return;
var vMethodName = "HandleConsolidatedData";
LogIt(vMethodName);
//BarDetails();
if (Time.Hour >= 16)
{
// LogIt("End of Day: No more activity");
return;
}
// LogMethodStart(vMethodName);
//SymbolDataDetails(vMethodName);
foreach (var symbolData in Data.Values)
{
// this check proves that this symbol was JUST updated prior to this OnData function being called
if (symbolData.IsReady && symbolData.WasJustUpdated(Time))
{
var output = String.Format("{0} - RSI - Current: {1}; Last: {2}; Second Last: {3}", symbolData.Symbol, symbolData.RSI, symbolData.RSI_History[0].Value, symbolData.RSI_History[1].Value);
LogIt(output);
// LogMethodStart(vMethodName + " - " + symbolData.Symbol);
/*
LogIt("Current Period Value: " + symbolData.Bars[0].Value );
LogIt(symbolData.SMA_Small.ToString());
LogIt("SMA Small Current: " + symbolData.SMA_Small.Current );
LogIt("SMA Small Details: " + symbolData.SMA_Small.ToDetailedString());
LogIt(symbolData.SMA_Large.ToString());
LogIt("SMA Large Current: " + symbolData.SMA_Large.Current );
LogIt("SMA Large Details: " + symbolData.SMA_Large.ToDetailedString());
*/
/*
LogIt("RSI: " + symbolData.RSI);
LogIt("RSI Current: " + symbolData.RSI_Lag[0]);
LogIt("RSI Previous: " + symbolData.RSI_Lag[1]);
*/
/*
foreach(var bar in symbolData.Bars)
{
var message = String.Format("{4} - Time: {0:ddd} {0} ; EndTime: {1:ddd} {1} ; Value: {2} ; Price: {3}", bar.Time, bar.EndTime, bar.Value, bar.Price, vMethodName);
// Debug(bar.ToString());
LogIt(message);
}
*/
}
}
}
/// <summary>
/// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets).
/// </summary>
/// <remarks>Method is called 10 minutes before closing to allow user to close out position.</remarks>
public override void OnEndOfDay()
{
// LogMethodStart("OnEndOfDay");
int i = 0;
// foreach (var kvp in Data.OrderBy(x => x.Value.Symbol))
foreach (var kvp in Data.Where(x => x.Value.Symbol == Symbols[PositionType.Inverse]))
{
// we have too many symbols to plot them all, so plot ever other
if (kvp.Value.IsReady && ++i%2 == 0)
{
// Plot(kvp.Value.Symbol.ToString(), kvp.Value.SMA_Small);
Plot(kvp.Value.Symbol.ToString(), kvp.Value.RSI);
}
}
}
private void SymbolDataDetails(string pMethodName)
{
// loop through each symbol in our structure
foreach (var symbolData in Data.Values)
{
//if (symbolData.IsReady)// && symbolData.WasJustUpdated(Time))
{
LogMethodStart(pMethodName + " - " + symbolData.Symbol);
foreach(var bar in symbolData.Bars)
{
var message = String.Format("{4} - Time: {0:ddd} {0} ; EndTime: {1:ddd} {1} ; Value: {2} ; Price: {3}", bar.Time, bar.EndTime, bar.Value, bar.Price, pMethodName);
// Debug(bar.ToString());
Debug(message);
}
}
}
}
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="data">TradeBars IDictionary object with your stock data</param>
public override void OnData(Slice data)
{
// handle_data();
/*
// loop through each symbol in our structure
foreach (var symbolData in Data.Values)
{
// this check proves that this symbol was JUST updated prior to this OnData function being called
if (symbolData.IsReady && symbolData.WasJustUpdated(Time))
{
// If the position is currently held
if((Portfolio[symbolData.Symbol].Quantity > 0))
{
}
}
}
*/
}
/*
public bool AreSymbolsReady()
{
foreach (var symbolData in Data.Values)
{
yield return new System.Threading.WaitUntil(() => (symbolData.IsReady && symbolData.WasJustUpdated(Time)));
// this check proves that this symbol was JUST updated prior to this OnData function being called
// if (symbolData.IsReady && symbolData.WasJustUpdated(Time))
// {
// }
}
return true;
}
*/
}
}namespace QuantConnect {
//
// Make sure to change "BasicTemplateAlgorithm" to your algorithm class name, and that all
// files use "public partial class" if you want to split up your algorithm namespace into multiple files.
//
//public partial class BasicTemplateAlgorithm : QCAlgorithm, IAlgorithm
//{
// Extension functions can go here...(ones that need access to QCAlgorithm functions e.g. Debug, Log etc.)
//}
//public class Indicator
//{
// ...or you can define whole new classes independent of the QuantConnect Context
//}
public partial class TestConsolidatedIndicator : QCAlgorithm, IAlgorithm
{
#region Log Method Start & End
private void LogMethodStart(string pMethodName)
{
return;
LogMethod(pMethodName);
}
private void LogMethodEnd(string pMethodName)
{
return;
LogMethod(pMethodName, false);
}
private void LogMethod(string pMethodName, bool pIsStart = true)
{
var vState = pIsStart ? "Start" : "End";
var vMessage = String.Format("Method: {0} {1} - {2:MM/dd/yy H:mm:ss:fff}", pMethodName, vState, Time);
LogIt(vMessage);
}
#endregion Log Method Start & End
private void LogIt(int pMessage)
{
LogIt(pMessage.ToString());
}
private void LogIt(string pMessage)
{
Debug(Time + " : " + pMessage);
// Log(Time + " : " + pMessage);
}
private bool IsTransactionDone()
{
return (Transactions.GetOpenOrders().Count == 0);
}
private bool HaveNoHoldingsIn(PositionType pPositionType)
{
return !HaveHoldingsIn(pPositionType);
}
private bool HaveHoldingsIn(PositionType pPositionType)
{
return (Portfolio[Symbols[pPositionType]].Quantity > 0);
}
private decimal Price(String pSymbol)
{
return Securities[pSymbol].Price;
// return Securities[pSymbol].GetLastData().Price;
}
}
}namespace QuantConnect {
/// <summary>
/// Contains data pertaining to a symbol in our algorithm
/// </summary>
public class ConsolidatedIndicator
{
/// <summary>
/// This symbol the other data in this class is associated with
/// </summary>
public readonly Symbol Symbol;
#region TradeBar properties
/// <summary>
/// A rolling window of data, data needs to be pumped into Bars by using Bars.Update( tradeBar ) and
/// can be accessed like:
/// mySymbolData.Bars[0] - most first recent piece of data
/// mySymbolData.Bars[5] - the sixth most recent piece of data (zero based indexing)
/// </summary>
public readonly RollingWindow<IBaseDataBar> Bars;
/// <summary>
/// The period used when population the Bars rolling window.
/// </summary>
public readonly TimeSpan BarPeriod;
#endregion TradeBar properties
/// <summary>
/// The simple moving average Small indicator for our symbol
/// </summary>
public SimpleMovingAverage SMA_Small;
/// <summary>
/// The simple moving average Large indicator for our symbol
/// </summary>
public SimpleMovingAverage SMA_Large;
/// <summary>
/// The Relative Strength Index indicator for our symbol
/// </summary>
public RelativeStrengthIndex RSI;
public readonly RollingWindow<IndicatorDataPoint> RSI_History;
// public readonly RollingWindow<RelativeStrengthIndex> RSI_History;
/// <summary>
/// Initializes a new instance of SymbolData
/// </summary>
public ConsolidatedIndicator(Symbol symbol, TimeSpan barPeriod, int windowSize)
{
Symbol = symbol;
BarPeriod = barPeriod;
Bars = new RollingWindow<IBaseDataBar>(windowSize);
RSI_History = new RollingWindow<IndicatorDataPoint>(3);
}
/// <summary>
/// Returns true if the most recent trade bar time matches the current time minus the bar's period, this
/// indicates that update was just called on this instance
/// </summary>
/// <param name="current">The current algorithm time</param>
/// <returns>True if this instance was just updated with new data, false otherwise</returns>
public bool WasJustUpdated(DateTime current)
{
return Bars.Count > 0 && Bars[0].Time == current - BarPeriod;
}
/// <summary>
/// Returns true if all the data in this instance is ready (indicators, rolling windows, ect...)
/// </summary>
public bool IsReady
{
get { return Bars.IsReady && SMA_Small.IsReady && SMA_Large.IsReady && RSI.IsReady; }
}
}
}