This has me wondering if Thinkorswim's 2000 limit is mandated by TD Ameritrade across all platforms. Can anyone confirm limits on the number of simultaneous requests through TD Ameritrade? Are there any such limits on Kinetick, if I subscribe? Is there anything else I can do to get reliable data from more stocks?
I'll include my script for reference. I'm trying to catch the huge, spontaneous dips on no news - basically what Timothy Sykes prefers doing now, but without all the research. The script itself seems to work well, though I need to refine my constraints as I collect more data. I only need the one column, so I remove all others to reduce the load. I record a small buffer myself, so there are no historic data requests in this script.
namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns { public class DipScanner : MarketAnalyzerColumn { private const int LOOKBACK = 12; // how many minutes of data to record private const float THRESHOLD = -10; // in percentage points private const bool PRINT = false; private int lastAlert; // last time the alert was triggered private int thresholdMultiple; // this script alerts again before the cooldown expires if percent change reaches a new multiple of THRESHOLD (-10%, -20%, -30%, etc.) // @TODO: pick a threshold for volume // @TODO: volume appears to be misleading during pre-market, showing yesterday's total private long dailyVolume; private bool cleanup; public List<int> times; // in minutes public List<double> prices; protected override void OnStateChange() { if (State == State.SetDefaults) { IsDataSeriesRequired = false; } else if (State == State.DataLoaded) { lastAlert = -10000; cleanup = true; times = new List<int>(); prices = new List<double>(); CurrentValue = 0; } else if (State == State.Terminated) { if (cleanup) { times = null; prices = null; } } } protected override void OnMarketData(MarketDataEventArgs data) { if (data.MarketDataType == MarketDataType.DailyVolume) { dailyVolume = data.Volume; // this should be zero on the first Evaluate since Last seems to come before DailyVolume } else if (data.MarketDataType == MarketDataType.Last) { int minute = Environment.TickCount / 60000; // TickCount is in milliseconds. up to 49.8 days after system startup Evaluate(minute, data.Price); } } private void Evaluate(int minute, double price) { // take only one snapshot per minute if (times.Count == 0 || times[times.Count-1] != minute) { times.Add(minute); prices.Add(price); // start at the end of the array and move forward. on the first minute at or above LOOKBACK, remove everything before it. protects against skips in market data. // if successful, calculate the percent change and alert if it falls below the threshold for (int i = times.Count-2; i >= 0; i--) { if (minute - times[i] >= LOOKBACK) { times.RemoveRange(0, i); prices.RemoveRange(0, i); float percent = (float)CalculatePercent(); // reset threshold if the alert cooldown has expired if (minute - lastAlert >= LOOKBACK) thresholdMultiple = 1; if (percent <= THRESHOLD * thresholdMultiple) { string emailMessage = "Volume: " + dailyVolume + " ... " + times[0] + "/" + prices[0] + " ... " + minute + "/" + price + " ... Threshold Multiple: " + thresholdMultiple; SendMail("[email protected]", "[" + Instrument.FullName + "] ... " + percent + "% at " + DateTime.Now.ToString("hh:mm tt"), emailMessage); string logMessage = "[" + Instrument.FullName + "] " + percent + "% at " + DateTime.Now.ToString("hh:mm tt") + " ... " + times[0] + "/" + prices[0] + " ... " + minute + "/" + price + " ... Volume: " + dailyVolume + " ... Threshold Multiple: " + thresholdMultiple; Log(logMessage, LogLevel.Information); Print(">>>> " + logMessage); lastAlert = minute; thresholdMultiple = (int)(percent / THRESHOLD) + 1; } CurrentValue = percent; break; } } } } private double CalculatePercent() { return (prices[prices.Count-1] - prices[0]) / prices[0] * 100; } } }
Comment