NexusFi: Find Your Edge


Home Menu

 





Design a DayTrader Scalping Order Flow Indicator


Discussion in Traders Hideout

Updated
      Top Posters
    1. looks_one hyperscalper with 136 posts (239 thanks)
    2. looks_two Chof with 22 posts (12 thanks)
    3. looks_3 Connor with 16 posts (8 thanks)
    4. looks_4 justtrader with 14 posts (8 thanks)
      Best Posters
    1. looks_one bobwest with 2 thanks per post
    2. looks_two hyperscalper with 1.8 thanks per post
    3. looks_3 SpeculatorSeth with 1 thanks per post
    4. looks_4 Chof with 0.5 thanks per post
    1. trending_up 47,235 views
    2. thumb_up 328 thanks given
    3. group 55 followers
    1. forum 248 posts
    2. attach_file 80 attachments




 
Search this Thread

Design a DayTrader Scalping Order Flow Indicator

  #81 (permalink)
 hyperscalper 
boise idaho
 
Experience: Advanced
Platform: NinjaTrader C# Custom
Broker: NinjaTrader LeeLoo Rithmic
Trading: Nasdaq Futures NQ/MNQ
Posts: 314 since Apr 2020
Thanks Given: 15
Thanks Received: 522


justtrader View Post
@hyperscalper
I wonder if you can modify the code to access the properties file only after it gets modified. And/Or perhaps check for file re-written with your timer and if true then read it. Most of the time the properties file will not change for a long time. Hence why read it all the time (save some CPU energy).

Some pseudo code:
if (lastModifiedTime != File.GetLastWriteTimeUtc) {
ReadFile();
lastModifiedTime = File.GetLastWriteTimeUtc;
}

Yes, of course, you could change the frequency with which property
file entries ("the properties") are read; or impose conditions, such as
detecting whether/when the file was changed, and then read again.... etc.
Surely, all that is possible.

But the actual read/parse and assignment of property values, is just such
extremely fast, and resource-sparing action; that I wouldn't worry about
it. You could change the Thread.Wait(....) timeout in milliseconds parameter
so that it does it every 5 minutes or so ( 5 * 60 * 1000 milliseconds).

I don't use a "Timer" mechanism; but it is simply a separate thread which
waits and times out, of course...

So, YES, certainly, you could try to read the file only when a change
in its contents occurred; but just checking for that "updated contents"
condition is most likely using resources, so are you really gaining
anything by adopting an approach like that ??

Don't get me wrong; I'm encouraging you to dive into Code and make
such improvements, but that would be an "optimization" which is
just entirely up to you. One thing I'd say is "Don't break the code,
trying to gain a millisecond of CPU"; keeping in mind that NinjaTrader
8 is Fully Multi-Threaded, and on modern processors, well they're
not Quantum Computers yet (LOL, like that's ever gonna happen) but
they are so fast that "in a blink of an eye" is really an old-fashioned
metaphor !! LOL

Time and Sales events, which we're processing; are not very "fast" events
in the grand scheme of things... On the other hand, where extreme
optimizations do make sense is when capturing Market Depth events,
which could be as high as 1 such event PER MILLISECOND; and then
you really need to start thinking "fancy optimizations" so you don't
get bottlenecks in your system !!! So "it's all relative" in a sense; and
of course keep your original copy; but if you want to change something,
again, that's THE WHOLE POINT of things is to get you deep into code.

[EDIT] RACE CONDITIONS can occur, which cause an error. The code I
provided should be "error tolerant". If the file were to be changed, in
the middle of a pass to read the property files, then at that particular
conjunction of events; my code is designed to be tolerant if an Exception
is thrown, and not just to crash. That's an unlikely "race condition"
but one you need to take account of... I just don't want my "property
reader thread" to crash !! LOL of course I haven't rigorously tested
that the code works, which is why "it ain't production code" necessarily
and is just "AS IS" ha ha ...

hyperscalper

Started this thread Reply With Quote
Thanked by:

Can you help answer these questions
from other members on NexusFi?
MC PL editor upgrade
MultiCharts
PowerLanguage & EasyLanguage. How to get the platfor …
EasyLanguage Programming
Exit Strategy
NinjaTrader
Better Renko Gaps
The Elite Circle
How to apply profiles
Traders Hideout
 
Best Threads (Most Thanked)
in the last 7 days on NexusFi
Spoo-nalysis ES e-mini futures S&P 500
29 thanks
Tao te Trade: way of the WLD
24 thanks
Just another trading journal: PA, Wyckoff & Trends
24 thanks
Bigger Wins or Fewer Losses?
21 thanks
GFIs1 1 DAX trade per day journal
16 thanks
  #82 (permalink)
 
justtrader's Avatar
 justtrader 
San Francisco, CA
 
Experience: Intermediate
Platform: Ninja Trader, TOS
Trading: es, rty, cl, gc, nq, ym
Posts: 183 since May 2011
Thanks Given: 104
Thanks Received: 173


hyperscalper View Post
Yes, of course, you could change the frequency with which property
file entries ("the properties") are read; or impose conditions, such as
detecting whether/when the file was changed, and then read again.... etc.
Surely, all that is possible.

But the actual read/parse and assignment of property values, is just such
extremely fast, and resource-sparing action; that I wouldn't worry about
it. You could change the Thread.Wait(....) timeout in milliseconds parameter
so that it does it every 5 minutes or so ( 5 * 60 * 1000 milliseconds).

I don't use a "Timer" mechanism; but it is simply a separate thread which
waits and times out, of course...

So, YES, certainly, you could try to read the file only when a change
in its contents occurred; but just checking for that "updated contents"
condition is most likely using resources, so are you really gaining
anything by adopting an approach like that ??

Don't get me wrong; I'm encouraging you to dive into Code and make
such improvements, but that would be an "optimization" which is
just entirely up to you. One thing I'd say is "Don't break the code,
trying to gain a millisecond of CPU"; keeping in mind that NinjaTrader
8 is Fully Multi-Threaded, and on modern processors, well they're
not Quantum Computers yet (LOL, like that's ever gonna happen) but
they are so fast that "in a blink of an eye" is really an old-fashioned
metaphor !! LOL

Time and Sales events, which we're processing; are not very "fast" events
in the grand scheme of things... On the other hand, where extreme
optimizations do make sense is when capturing Market Depth events,
which could be as high as 1 such event PER MILLISECOND; and then
you really need to start thinking "fancy optimizations" so you don't
get bottlenecks in your system !!! So "it's all relative" in a sense; and
of course keep your original copy; but if you want to change something,
again, that's THE WHOLE POINT of things is to get you deep into code.

[EDIT] RACE CONDITIONS can occur, which cause an error. The code I
provided should be "error tolerant". If the file were to be changed, in
the middle of a pass to read the property files, then at that particular
conjunction of events; my code is designed to be tolerant if an Exception
is thrown, and not just to crash. That's an unlikely "race condition"
but one you need to take account of... I just don't want my "property
reader thread" to crash !! LOL of course I haven't rigorously tested
that the code works, which is why "it ain't production code" necessarily
and is just "AS IS" ha ha ...

hyperscalper


Well put.

Thank you for reviewing my request and explaining the read process. I will just go on without making any changes. I will test your latest code on Monday. I am hoping it will be better than another simple crude script I have that simulates big lots.

JT

TWYS NWYT (Price Advertises Opportunity; Time Regulates it; Volume Measures its Success/Failure ---- Dalton)
Reply With Quote
Thanked by:
  #83 (permalink)
 hyperscalper 
boise idaho
 
Experience: Advanced
Platform: NinjaTrader C# Custom
Broker: NinjaTrader LeeLoo Rithmic
Trading: Nasdaq Futures NQ/MNQ
Posts: 314 since Apr 2020
Thanks Given: 15
Thanks Received: 522



justtrader View Post
Well put.

Thank you for reviewing my request and explaining the read process. I will just go on without making any changes. I will test your latest code on Monday. I am hoping it will be better than another simple crude script I have that simulates big lots.

JT

Well, look, I sprinkled in a lot of "unconventional" things, that most
indicator writers might not do...

Now, here's an idea; As we move through the session, let's consider
that we could have 3 profile files for the 3 "periods" of activity or
volatility.

Without restarting the indicator, you could just copy one of the 3 profile
files onto the primary file name to "shift gears" and tune the indicator
to the current conditions.

copy profileA.txt profile.txt
or, copy profileB.txt profile.txt
or, copy profileC.txt profile.txt

It's a little unconventional.

But we shouldn't be thinking about these "Nuts 'n Bolts" issues; when the
primary goal here is to extract a "real trade signal".

WHAT IF: We develop a simple Price MACD, and then we combine Price
Deflection with Risk in some advantageous way??? Those are the kind
of ideas we should be moving toward, just as an idea.

That's where we should be focusing our thinking !! Ever onwards

hyperscalper

Started this thread Reply With Quote
Thanked by:
  #84 (permalink)
 
justtrader's Avatar
 justtrader 
San Francisco, CA
 
Experience: Intermediate
Platform: Ninja Trader, TOS
Trading: es, rty, cl, gc, nq, ym
Posts: 183 since May 2011
Thanks Given: 104
Thanks Received: 173


hyperscalper View Post
Well, look, I sprinkled in a lot of "unconventional" things, that most
indicator writers might not do...

Now, here's an idea; As we move through the session, let's consider
that we could have 3 profile files for the 3 "periods" of activity or
volatility.

Without restarting the indicator, you could just copy one of the 3 profile
files onto the primary file name to "shift gears" and tune the indicator
to the current conditions.

copy profileA.txt profile.txt
or, copy profileB.txt profile.txt
or, copy profileC.txt profile.txt

It's a little unconventional.

But we shouldn't be thinking about these "Nuts 'n Bolts" issues; when the
primary goal here is to extract a "real trade signal".

WHAT IF: We develop a simple Price MACD, and then we combine Price
Deflection with Risk in some advantageous way??? Those are the kind
of ideas we should be moving toward, just as an idea.

That's where we should be focusing our thinking !! Ever onwards

hyperscalper

Good ideas. I will keep those in mind as I continue to research & analyze to get signals with these scripts.

In that regard I was comparing 2 instances of the TradeFlowRisk (I disabled the trailing dots) against one instance of the TradeFlowTutorial. (Please see attached chart and parameter details below). I was expecting the Blue and the Yellow plots to be fairly identical. But they are way different.

For some reason I am beginning to see more potential of getting signals using the TradeFlowTutorial, which is oscillating faster then the TradeFlowRisk. (By the way, the trade shown was not made based on these scripts, it was my AutoTrader using other type input signals).

Down the road, as my next task, I will be looking at incorporating and running 2 instances of either TradeFlowRisk or TradeFlowTutorial within the same script. Seems like a huge challenge for me, not having the expert programing skills you have.


Parameters used on the chart:
Green:
# your comment lines
RISK_THRESHOLD=10
RETENTION_SECONDS=120
MULTIPLIER=1
BIGLOT_MINIMUM=2
TAPER_SIZE=True
SUPER_SPIKE_THRESHOLD_RATIO=1.8

Blue:
# your comment lines
RISK_THRESHOLD=10
RETENTION_SECONDS=60
MULTIPLIER=1
BIGLOT_MINIMUM=5
TAPER_SIZE=True
SUPER_SPIKE_THRESHOLD_RATIO=1.8


Yellow: (Same RETENTION_SECONDS and BIGLOT_MINIMUM as the BLUE plot).
# comment line
RETENTION_SECONDS=60
MULTIPLIER=1
BIGLOT_MINIMUM=5
TAPER_SIZE=true



TWYS NWYT (Price Advertises Opportunity; Time Regulates it; Volume Measures its Success/Failure ---- Dalton)
Reply With Quote
  #85 (permalink)
 hyperscalper 
boise idaho
 
Experience: Advanced
Platform: NinjaTrader C# Custom
Broker: NinjaTrader LeeLoo Rithmic
Trading: Nasdaq Futures NQ/MNQ
Posts: 314 since Apr 2020
Thanks Given: 15
Thanks Received: 522


justtrader View Post

For some reason I am beginning to see more potential of getting signals using the TradeFlowTutorial, which is oscillating faster then the TradeFlowRisk. (By the way, the trade shown was not made based on these scripts, it was my AutoTrader using other type input signals).


I'm glad you're getting into it. That was the whole reason for presenting
some code.

It is generally true that Net Inventory fluctuations do correlate with local
tops and bottoms. However, the concept of Risk is more significant, so
I'd encourage you to focus on that.

You can quantify "How much Risk?" in dollars, if you know the Risk delta
distance from Market Price in Ticks, times the Value of a Tick, times
a factor proportional to Net Inventory imbalance.

As I've said before, this ain't full Inventory Analysis, but it approximates
pretty well, and does relate to the concept of "How much Risk" Market Maker
is experiencing on the Time Frames which are described by the Retention
Interval.

Just sayin'... For a simple indicator; on a short term basis, it can be very
useful.

It addresses the core concept that Market Maker uses Retail Players to
"take a position" against them on the Long and Short sides, and dips
into "pseudo risk" at the tops and bottoms.

I say "pseudo risk" because MM is really taking no risks at all; as She manipulates
Retail players into Buyers and Sellers who are basically chasing the price trend.

hyperscalper

Started this thread Reply With Quote
Thanked by:
  #86 (permalink)
 hyperscalper 
boise idaho
 
Experience: Advanced
Platform: NinjaTrader C# Custom
Broker: NinjaTrader LeeLoo Rithmic
Trading: Nasdaq Futures NQ/MNQ
Posts: 314 since Apr 2020
Thanks Given: 15
Thanks Received: 522

SOME REMARKS ON TRADE FLOW ANALYSIS

So, many of you know this as "Order Flow" Analysis, but I'll
make the explicit distinction that Trades (Time and Sales)
are transactional exchanges of Inventory, so should be called
Trade Flow Analysis.

Orders are "on The Book" or the "Depth of Market" but do not
represent transactions; rather, they represent an "interest"
in making a transaction; a Bid or an Offer which may not be
taken. So "real Order Flow Analysis" is far beyond the scope
of this indicator; and beyond most Traders' reach; as it will
depend upon the Quality of the Market Depth.

Most brokerages give a "cheap" or "aggregated" or "limited"
Market Depth. Why? Traders don't really use it; and so there's
no reason to offer it. The Premium Market Data Feeds, such
as Rithmic and others; which might be used for Order Flow
or "DOM Analysis" are beyond what Traders are even interested
in; as they wouldn't know what to do with it..... 'NUFF SAID.

SO THIS IS NOT ORDER FLOW ANALYSIS, but it is Trade Flow
Analysis, and that depends upon using the Time and Sales only.

We can assume that most brokerages/data feeds provide an
Accurate Time and Sales; so this Indicator can be used by just
about any Trader.

WHAT IS THE VALUE OF THIS INDICATOR?

I may have said elsewhere; that Trade Flow, which yields a Relative
Net Inventory Imbalance, and some reference to Risk; is....

"A Situational Analysis". What does that mean?

It means that we can say to ourselves "Well, Market Maker is taking
Risk here" and that means She may be looking to Turn the Market.

BUT IT DOES NOT necessarily provide you with PRECISION as to
exactly where the Market Trend will turn.

Depending upon the Precision requirements for your own trading;
it is either Very Useful; or perhaps Inadequate if you require Very High
Precision on where the Trend Top or the Bottom exists.

SO IT'S USELESS? No, not at all... It's a very important "situational
variable" which informs you of Market Maker's Relative Inventory
"situation" and also potential "Risk" situation; and "that ain't Nuthin'"...

By the very nature of trading, Market Maker has "very deep pockets"
and so, when a Market is strongly trending; MM can push well beyond
any "reasonable" turning point.

So understand that this Technical Indicator; like many other technicals,
can be "swamped" by EXTERNAL factors (like the Market Open, etc
or News Events)... But when the Market is NOT primarily being driven
by external factors, then Technical Analysis such as this is Very Good,
at predicting turning points of a Market.

YOUR COMMENTS WELCOME !!! I'm trying to encourage you to jump
into code; but also to think about how you can design an Indicator
which implements Your Theory about what may be Predictive.

It will inspire a handful of you; if so, then it's worth the trouble, and
I'd encourage you to think about the Strengths and the Weaknesses
of any such Technical Indicator... and then think how it can be improved.

SPOILER ALERT: Everything can be significantly improved...

[EDIT] I'm running this thing myself... And attached is a screenshot of
how I use it.

Parameter files are:
# comment line
RISK_THRESHOLD=10
RETENTION_SECONDS=180
MULTIPLIER=1
BIGLOT_MINIMUM=3
TAPER_SIZE=True
SUPER_SPIKE_THRESHOLD_RATIO=2.8

and...
# comment line
RISK_THRESHOLD=12
RETENTION_SECONDS=600
MULTIPLIER=1
BIGLOT_MINIMUM=2
TAPER_SIZE=True
SUPER_SPIKE_THRESHOLD_RATIO=10.0

Note that I adjust the left margin, to fix the scaling so the Net Inventory zero
point is halfway, etc. NinjaTrader 8 charts are nicely flexible, so that
these "dragged overlays" can be adjusted to be less confusing.

[EDIT2] Note that the Dots showing RISK are relative to Price; and not relative
to the Net Inventory lines. Those written on top of Price, are the "Big Lot" sizes
which tend to occur also at tops and bottoms.

Here's my latest code just in case:
 
Code
#region Using declarations
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Gui.SuperDom;
using NinjaTrader.Gui.Tools;
using NinjaTrader.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.Core.FloatingPoint;
using NinjaTrader.NinjaScript.DrawingTools;
#endregion

//This namespace holds Indicators in this folder and is required. Do not change it. 
namespace NinjaTrader.NinjaScript.Indicators
{
	/* Code is freely offered as tutorial examples; but is "As Is"
	and the user is fully responsible for the consequences of any
	changes.  The purpose is to encourage modifications; but be sure
	you have saved stable versions, perhaps on a daily basis, in case you break it.
	*/
	public class TradeFlowRisk : Indicator
	{
		protected override void OnStateChange()
		{
			if (State == State.SetDefaults)
			{
				Description									= @"in Futures.IO an exercise in addding Risk to a simple Trade Flow analyzer.";
				Name										= "TradeFlowRisk";
				Calculate									= Calculate.OnBarClose;
				IsOverlay									= false;
				DisplayInDataBox							= false;
				DrawOnPricePanel							= true;
				DrawHorizontalGridLines						= false;
				DrawVerticalGridLines						= false;
				PaintPriceMarkers							= true;
				ScaleJustification							= NinjaTrader.Gui.Chart.ScaleJustification.Left;
				//Disable this property if your indicator requires custom values that cumulate with each new market data event. 
				//See Help Guide for additional information.
				IsSuspendedWhileInactive					= false; // always run
				FileName					= "uniqueTradeFlowProps.txt";
				AltColor					= false; 
			}
			else if (State == State.Configure)
			{
				indexCounter = 0; // start at zero
				// index named variables automatically assign in order
				AddPlot(new Stroke(Brushes.Black), PlotStyle.Hash, "zeroIndex"); // index
				zeroIndex = indexCounter++; // starts with zero; plot idx
				Plots[zeroIndex].Width = 2;
				
				AddPlot(new Stroke(AltColor?Brushes.Blue:Brushes.Purple), PlotStyle.Line, "inventory"); // index
				inventoryIndex = indexCounter++; // starts with zero; plot idx
				Plots[inventoryIndex].Width = 4;
				
			}
			else if (State == State.Realtime)
			{
				init();
				isReady = true;
			}
			else if (State == State.Terminated)
			{
				isReady = false;
				deInit();
			}
		}
		
		#region Properties
		[NinjaScriptProperty]
		[Display(Name="FileName.txt", Description="Properties file in default folder", Order=1, GroupName="Parameters")]
		public string FileName
		{ get; set; }

		[NinjaScriptProperty]
		[Display(Name="Alternate colors", Description="blue/orange vs red/green", Order=2, GroupName="Parameters")]
		public bool AltColor
		{ get; set; }
		#endregion

		private int indexCounter = 0;
		
		private int zeroIndex = 0; // assigned in config
		private int inventoryIndex = 0;
		
		private bool isReady = false;
		
		#region Startup and Initialization
		private void init() {
			customRootPath = Environment.ExpandEnvironmentVariables(
					"%USERPROFILE%\\Documents\\NinjaTrader 8\\bin\\Custom\\");
			myIntegrator = new VolumePriceExpiryIntegrator(this, 1000, retentionSecs);
			// allocate objects
			if (myPropertyReader==null) {
				myPropertyReader = new SimplePropertyReader(this);
				myPropertyReader.setPropertiesFileNameDotTxt(FileName);
				myPropertyReader.setPropertiesSamplingSecs(30); // file sampling frequency
				// now assign default property values
				myPropertyReader.setKeyValue(riskThresholdTicksProp, ""+riskThresholdTicks); // stringify it
				myPropertyReader.setKeyValue(retentionSecsProp, ""+retentionSecs); // stringify it
				myPropertyReader.setKeyValue(multiplierProp, ""+multiplier);
				myPropertyReader.setKeyValue(bigLotMinimumProp, ""+bigLotMinimum);
				myPropertyReader.setKeyValue(taperSizeProp, ""+taperSize);
				myPropertyReader.setKeyValue(superSpikeThresholdRatioProp, ""+superSpikeThresholdRatio);
				myPropertyReader.start();
			}
			onBarUpdateLimiter = new SimpleRateLimiter(20); // milliseconds
			superSpikeReferenceLimiter = new SimpleRateLimiter(10000);
		}
		#endregion
		
		#region Synchronization and PROPERTY Management
		// begin of synchronization support
		// use getUpdateMutex()
        private object updateMutex = new object();
		
		private SimplePropertyReader myPropertyReader = null;

        // asynchronous property updates use this
        // synchronization mechanism
		// in Visual Studio, of course, we'd use
		// interface definitions, and multiple
		// files, but we're staying in a single file
		// for usage within the NinjaScript environment
        protected object getUpdateMutex() {
            return updateMutex; 
        }
		
		// CONTROL VARIABLES MODIFIABLE BY PROPERTY MANAGER
		private static string retentionSecsProp = "RETENTION_SECONDS";
		private int retentionSecs = 60; // default 60 seconds
		private static string multiplierProp = "MULTIPLIER";
		private double multiplier = 1.0; // default unity multiplier
		private static string riskThresholdTicksProp = "RISK_THRESHOLD";
		private double riskThresholdTicks = 3.0; // 
		private static string bigLotMinimumProp = "BIGLOT_MINIMUM";
		private int bigLotMinimum = 2; // default unity multiplier
		private static string taperSizeProp = "TAPER_SIZE";
		private bool taperSize = true; // default useTaper
		private static string superSpikeThresholdRatioProp = "SUPER_SPIKE_THRESHOLD_RATIO";
		private static double superSpikeThresholdRatio = 4.0;
		
		protected void updatePropertiesCallback() {
			if (myPropertyReader==null) return;
			lock(getUpdateMutex()) {
				// a thread which already holds the lock
				// will always get the same lock again....
			// the file-based property manager will
			// firstly lock(getUpdateMutex())
			// and then it will call this routine
			// where your property values can
			// be updated safely
			//	myPropertyReader.setKeyValue(retentionSecsProp, retentionSecs);
			//	myPropertyReader.setKeyValue(multiplierProp, multiplier);
			//	myPropertyReader.setKeyValue(bigLotMinimumProp, bigLotMinimum);
			//if ( !myPropertyReader.hasChanged()) return;	
			try {
				int retentionSecsTmp = myPropertyReader.getIntegerProperty(retentionSecsProp);
				if (retentionSecsTmp<10) retentionSecsTmp = 10; // ten sec min
				retentionSecs = retentionSecsTmp;
				myIntegrator.setRetentionSecs(retentionSecs); // change the Integrator
			} catch (Exception ex) { ninjaPrint2(retentionSecsProp+" error!", true); } // do nothing
			try {
				double multiplierTmp = myPropertyReader.getDoubleProperty(multiplierProp);
				if (multiplierTmp<0.1) multiplierTmp = 0.1;
				multiplier = multiplierTmp;
				// used in OnBarUpdate
			} catch (Exception ex) {ninjaPrint2( multiplierProp+" error!", true);} // do nothing
			try {
				int bigLotMinimumTmp = myPropertyReader.getIntegerProperty(bigLotMinimumProp);
				if (bigLotMinimumTmp<1) bigLotMinimumTmp = 1;
				bigLotMinimum = bigLotMinimumTmp;
				// used when reporting Signed Trade Volumes
			} catch (Exception ex) {ninjaPrint2( bigLotMinimumProp+" error!", true);} // do nothing
			try {
				taperSize = myPropertyReader.getBooleanProperty(taperSizeProp);
				myIntegrator.setUseTaper(taperSize);
			} catch (Exception ex) {ninjaPrint2( taperSizeProp+" error!", true);} // do nothing
			try {
				riskThresholdTicks = myPropertyReader.getDoubleProperty(riskThresholdTicksProp);
				if (riskThresholdTicks<0) riskThresholdTicks = 0; // no negatives
			} catch (Exception ex) {ninjaPrint2( riskThresholdTicksProp+" error!", true);} // do nothing
			try {
				double superSpikeThresholdRatioTmp = myPropertyReader.getDoubleProperty(superSpikeThresholdRatioProp);
				if (superSpikeThresholdRatioTmp<1) superSpikeThresholdRatioTmp = 1;
				superSpikeThresholdRatio = superSpikeThresholdRatioTmp;
				// used when reporting Super Spike Trade Volumes
			} catch (Exception ex) {ninjaPrint2( superSpikeThresholdRatioProp+" error!", true);} // do nothing
				
				
			} // end of any property value updates
		}
		// end of synchronization support
		#endregion

		#region NinjaScript Output Window printing
		private static bool USE_NINJA_PRINT = true; // suppress ALL printing
		
        private void ninjaPrint(string msg) { // primary tab
            if (!USE_NINJA_PRINT) return;
            PrintTo = PrintTo.OutputTab1;
            Print(msg);
        }

		private void ninjaPrint2(string msg) {
			ninjaPrint2(msg, false); // do not force
		}
        
        private void ninjaPrint2(string msg, bool force) { // secondary tab
            if ( !force && !USE_NINJA_PRINT) return;
            PrintTo = PrintTo.OutputTab2;
            Print(msg);
        }
		#endregion
		
		#region The Trade Price Volume (tapering) Integrator
		private VolumePriceExpiryIntegrator myIntegrator = null;
		
		/* This version will enforce a strict moving
		time window, and optionally taper the trailing
		portion of that window gradually to zero to
		avoid artifacts associated with size leaving
		the time window*/
		protected /*inner*/ class VolumePriceExpiryIntegrator {
			private double[] volumeArray = null;
			private double[] priceArray = null;
			private DateTime[] timestampArray = null;
			// these are treated as "circular buffers"
			bool useTaperFlag = false; // TODO
			int size = 0;
			int ptr = 0;
			int _expiryMsecs = 60000;
			TradeFlowRisk parent = null;
			public VolumePriceExpiryIntegrator(TradeFlowRisk parentRef, int sizeArg, int expirySecsArg) { // constructor
				parent = parentRef;
				size = sizeArg;
				_expiryMsecs = 1000 * expirySecsArg;
				volumeArray = new double[size];
				priceArray = new double[size];
				timestampArray = new DateTime[size];
				DateTime now = DateTime.UtcNow;
				for(int i=0; i<size; i++) {
					volumeArray[i] = 0;
					priceArray[i] = 0;
					timestampArray[i] = now;
				}
			}
			
			public void addVolumePriceData(double volumeArg, double priceArg) {
				// RULE: ptr should always index the
				// next valid array index, not the
				// most recent one.  ptr always
				// increments.
				if (ptr>=size) {
					ptr = 0;
				}
				timestampArray[ptr] = DateTime.UtcNow;
				priceArray[ptr] = priceArg; // including price
				volumeArray[ptr++] = volumeArg; // increment after usage
				if (ptr>=size) {
					ptr = 0;
				}
			}
			
			public void setRetentionSecs(int seconds) {
				_expiryMsecs = seconds * 1000;
			}
			
			public void setUseTaper(bool flag) {
				useTaperFlag = flag;
			}
			
			public double getAverageVolumeUnsigned() {
				return getSumVolumeImpl(0, true); // return AVERAGE UNSIGNED VOLUME
			}
			
			public double getAverageVolumeUnsigned(int msecsOverride) {
				return getSumVolumeImpl(msecsOverride, true); // return AVERAGE UNSIGNED VOLUME
			}
			
			public double getAverageVolumeInterval(int ageMsecsOverride) {
				return getSumVolumeImpl(ageMsecsOverride, true); // return AVERAGE UNSIGNED VOLUME
			}
			
			public double getSumVolume() {
				return getSumVolumeImpl(0, false); // not average, SIGNED Integration
			}
			
			// this has a dual-purpose, as well as optional retention interval override
			private double getSumVolumeImpl(int ageMsecsOverride, bool returnAvgUnsignedFlag) {
				// must work backwards in time from most
				// recent until items are expired, or we
				// traverse the entire circular ring buffer
				DateTime now = DateTime.UtcNow; // this is REAL TIME ONLY
				double sum = 0;
				int n = 0;
				int tmpPtr = ptr - 1; // most recent
				if (tmpPtr<0) tmpPtr = size - 1;
				else if (tmpPtr>=size) tmpPtr = 0; // array bounds
				int _expiryMsecsLocal = _expiryMsecs;
				if ( ageMsecsOverride > 0) { // substitute the given window time
					_expiryMsecsLocal = ageMsecsOverride;
				}
				int taperBeginAge = (int)(_expiryMsecsLocal * 0.75); // if tapering...
				bool expired = false;
				// always using tmpPtr here !
				while( !expired && tmpPtr!=ptr) {
					DateTime ts = timestampArray[tmpPtr];
					double item = volumeArray[tmpPtr--];
					// REMINDER: we are moving from most recent
					// backward in time here !!
					if (tmpPtr<0) tmpPtr = size - 1; // array bounds
					TimeSpan elapsed = ( now - ts );
					int ageMsecs = (int)elapsed.TotalMilliseconds;
					if ( ageMsecs > _expiryMsecsLocal ) {
						expired = true; continue; // and exit loop
					}
					if ( tmpPtr == ptr ) {
						// we exhaused all of our circular buffer (unlikely)
						expired = true; continue; // and exit loop
					}
					double finalItem = item;
					if (useTaperFlag) { // taper last 25% of time window
						// OK, so we'll taper the oldest 25% of the
						// time window trade volume linearly down to zero
						// e.g. Expiry might be 10000, so begin tapering beyond age 7500
						if (ageMsecs > taperBeginAge) { // e.g. from 7500 msecs into a 10000 max window
							int numerator = _expiryMsecsLocal - ageMsecs; // progressing 2500 toward zero
							if (numerator<0) numerator = 0;
							int denominator = _expiryMsecsLocal - taperBeginAge; // e.g. 10000 - 7500 = 2500
							double ratio = (double)numerator / (double)denominator;
							finalItem *= ratio; // LINEAR TAPER
						}
					}
					sum += (returnAvgUnsignedFlag? Math.Abs(finalItem) : finalItem); 
					// summation of signed (tapered) trade volume OR unsigned Average volume
					n++;
				}
				double returnValue = 0;
				if (returnAvgUnsignedFlag) {
					returnValue = ( n>0? sum/n : 0 );
				}
				else { // signed summation
					returnValue = sum;
				}
				return returnValue;
			}

			public static int VWAP_POSITIVE_VOLUMES = 3; // arbitrary constants
			public static int VWAP_NEGATIVE_VOLUMES = 4;
			public static int VWAP_ALL_VOLUMES = 5;
			
			public double getVwapPrice(int calcMethod) { // throws Exception for ZERO valued prices
				// must work backwards in time from most
				// recent until items are expired, or we
				// traverse the entire circular ring buffer
				DateTime now = DateTime.UtcNow; // this is REAL TIME ONLY
				double sumV = 0; // SUM( abs(volume) )
				double sumPV = 0; // SUM (price times abs(volume))
				int n = 0;
				int tmpPtr = ptr - 1; // most recent
				if (tmpPtr<0) tmpPtr = size - 1;
				//else if (tmpPtr>=size) tmpPtr = 0; // array bounds
				int taperBeginAge = (int)(_expiryMsecs * 0.75); // if tapering...
				bool expired = false;
				// always using tmpPtr here !
				while( !expired && tmpPtr!=ptr) {
					DateTime ts = timestampArray[tmpPtr];
					double price = priceArray[tmpPtr];
					double volume = volumeArray[tmpPtr--];
					if (tmpPtr<0) tmpPtr = size - 1; // array bounds
					if (price==0) {
						//parent.ninjaPrint2("Price is zero");
						continue;
					}
					if (calcMethod!=VWAP_ALL_VOLUMES) {
						if (calcMethod==VWAP_POSITIVE_VOLUMES) {
							if (volume<0) continue; // ignore NEGATIVE
						}
						else if (calcMethod==VWAP_NEGATIVE_VOLUMES) {
							if (volume>0) continue; // ignore POSITIVE
						}
					}
					volume = Math.Abs(volume); // always Positive weightings
					// REMINDER: we are moving from most recent
					// backward in time here !!
					TimeSpan elapsed = ( now - ts );
					int ageMsecs = (int)elapsed.TotalMilliseconds;
					if ( ageMsecs > _expiryMsecs ) {
						expired = true; continue; // and exit loop
					}
					if ( tmpPtr == ptr ) {
						// we exhaused all of our circular buffer (unlikely)
						expired = true; continue; // and exit loop
					}
					double taperedVolume = volume;
					if (false) { // do not taper weights (useTaperFlag) { // taper last 25% of time window
						// OK, so we'll taper the oldest 25% of the
						// time window trade volume linearly down to zero
						// e.g. Expiry might be 10000, so begin tapering beyond age 7500
						if (ageMsecs > taperBeginAge) { // e.g. from 7500 msecs into a 10000 max window
							int numerator = _expiryMsecs - ageMsecs; // progressing 2500 toward zero
							if (numerator<0) numerator = 0;
							int denominator = _expiryMsecs - taperBeginAge; // e.g. 10000 - 7500 = 2500
							double ratio = (double)numerator / (double)denominator;
							taperedVolume *= ratio; // LINEAR TAPER
						}
					}
					++n;
					sumV += taperedVolume; // summation of signed (tapered) trade volume
					sumPV += ( taperedVolume * price );
				}
				// a zero return PRICE means no data
				double vwap = sumV==0 ? 0 : sumPV / sumV;
				//parent.ninjaPrint2("vwap data items: "+n+" price: "+vwap);
				return vwap;
			}

		}
		#endregion

		#region Calculations and Utilities
        private static double EXP = 0.75; // exponent less than 1 and >0.5

        // non-linear signed size compression
        private static double compressSizeMinOneSigned(double valueArg)
        {
			if ( EXP > 1 ) EXP = 1.0; // no compression at all
			if ( EXP < 0.5 ) EXP = 0.5; // square root
			bool isNegative = ( valueArg < 0 );
            double absValue = Math.Abs(valueArg); // calculate on positive
            if (absValue < 1) absValue = 1; // enforce minimum of size 1 (contract)
            double x = Math.Pow(absValue + 0.0001, EXP); // just adding a smidgeon
            // 1^0 = 1 so 1 should be the minimum possible
            if (x < 1) x = 1;
            return ( isNegative? -x : x); // restore the sign
        }
		#endregion

		#region Shutdown de-Initialization
		
		private void deInit() {
			myIntegrator = null;
			if (myPropertyReader!= null) {
				myPropertyReader.stop(); // kill the thread
				myPropertyReader = null;
			}
			onBarUpdateLimiter = null;
			superSpikeReferenceLimiter = null;
			// stop and deallocate objects
		}
		#endregion
		
		private static bool LOG_RAW_VOLUME = true;
		
		private int lastRawTradeVol = 0;
		
		#region Processing Trade Flow
		private void reportTrade(int tradeRawVol, double price) {
			if (Math.Abs(tradeRawVol) < bigLotMinimum) return; // reject volume
			if ( LOG_RAW_VOLUME ) {
				if (tradeRawVol>0) {
					ninjaPrint("Retail Buy "+tradeRawVol+" @"+price);
				}
				else {
					ninjaPrint("       Retail Sell "+tradeRawVol+" @"+price);
				}
			}
			lastRawTradeVol = tradeRawVol;
			double compressedVol = compressSizeMinOneSigned( tradeRawVol );
			// Super Spike Volumes test here
			//if (Math.Abs(compressedVol)>superSpikeThreshold*getLastReferenceVolume()) {
			if (Math.Abs(compressedVol)> (superSpikeThresholdRatio*getLastReferenceVolume())) {
				if (compressedVol>0) wasSuperSpikeBuy = true;
				else wasSuperSpikeSell = true;
			}
			reportToIntegrator( compressedVol, price );
		}
		
		private void reportToIntegrator(double compressedVolArg, double priceArg) {
			myIntegrator.addVolumePriceData(compressedVolArg, priceArg);
		}
		#endregion
		
		private double lastBidPrice = 0;
		private double lastAskPrice = 0;
		private string customRootPath = null;
		
		private static int REFERENCE_MSECS = 10 * 60 * 1000; // 5 mins in Msecs
		private double lastSuperSpikeReferenceVolume = 50; // start with this pending warm-up
		private SimpleRateLimiter superSpikeReferenceLimiter = null;
		
		public double getSuperSpikeReferenceVolume() {
			if ( !isPrimed() || !superSpikeReferenceLimiter.allowUpdate() ) {
				return lastSuperSpikeReferenceVolume;
			}
			// when primed, the Super Spike reference volume updates
			lastSuperSpikeReferenceVolume = myIntegrator.getAverageVolumeInterval(REFERENCE_MSECS) ;
			return lastSuperSpikeReferenceVolume;
		}
		
		private double superSpikeThreshold = 8;
		
		private double lastReferenceVolume = 5; // updated
		private int lastReferenceVolumeCounter = 0;
		
		
		private double getLastReferenceVolume() {
			++lastReferenceVolumeCounter; if (lastReferenceVolumeCounter>10000) lastReferenceVolumeCounter=0;
			if (lastReferenceVolumeCounter%50==0) {
				lastReferenceVolume = myIntegrator.getAverageVolumeUnsigned(REFERENCE_MSECS);
				//ninjaPrint2("lastRefVol: "+lastReferenceVolume, true);
			}
			return lastReferenceVolume;
		}

		#region OnMarketUpdate callback
		protected sealed override void OnMarketData(MarketDataEventArgs marketDataUpdate)
		{
			if ( !isReady ) return;
			lock(getUpdateMutex()) { // wait to allow property updates now
				MarketDataType type = marketDataUpdate.MarketDataType;
				if (type==MarketDataType.Last) { // Trade
					if (lastBidPrice==0) return;
					if (lastAskPrice==0) return;
					if (lastAskPrice <= lastBidPrice) return;
					double tradePrice = marketDataUpdate.Price; // price of the Trade
					int tradeVolume = (int)marketDataUpdate.Volume;
					double mid = 0.5 * ( lastAskPrice + lastBidPrice ); // mid price
					if (tradePrice < mid ) {
						// trade toward BID negative Retail Sell
						reportTrade( -tradeVolume, tradePrice );
					}
					else {
						// trade toward ASK/OFFER positive Retail Buy
						reportTrade( tradeVolume, tradePrice );
					}
				}
				else if (type==MarketDataType.Bid) {
					lastBidPrice = marketDataUpdate.Price; // BID price
				}
				else if (type==MarketDataType.Ask) {
					lastAskPrice = marketDataUpdate.Price; // ASK/OFFER price
				}
			} // end lock
			return;
		}
		#endregion
		
		private int primedCounter = 0;
		private bool isPrimedFlag = false;
		
		private bool isPrimed() {
			return isPrimedFlag;
		}
		
		// wait some Bars before some functions active
		// in Real Time, often you want some data before
		// doing some stuff
		private void incrementPrimedCount() {
			if (isPrimed()) return;
			++primedCounter;
			if (primedCounter>20) isPrimedFlag = true;
		}
		
		private double lastInventory = 0;
		
		int tagCounter = 0;
		
		private string getShortTag() {
			return "SRisk"+tagCounter++;
		}
		
		private string getLongTag() {
			return "LRisk"+tagCounter++;
		}
		
		private Brush shortRiskBrushAlt = Brushes.MediumBlue;
		private Brush longRiskBrushAlt = Brushes.DarkOrange;
		private Brush shortRiskBrush = Brushes.Lime;
		private Brush longRiskBrush = Brushes.Red;
		
		private Brush superSpikeSellBrush = Brushes.Fuchsia;
		private Brush superSpikeBuyBrush = Brushes.Green;
		private bool wasSuperSpikeBuy = false;
		private bool wasSuperSpikeSell = false;

		private SimpleRateLimiter onBarUpdateLimiter = null; // init and deInit
		
		private static bool DBG = false;
		protected sealed override void OnBarUpdate()
		{
			if ( !isReady ) return; // must have
			bool doUpdate = onBarUpdateLimiter.allowUpdate();
			if (IsFirstTickOfBar) doUpdate = true; // allow on first tick of bar
			if ( !doUpdate ) return; // rate limited
			incrementPrimedCount();
			if ( !isPrimed() ) return;
			// isFirstTickOfBar can be used to clear
			// any inter-bar cumulative variables
			lock(getUpdateMutex()) { // no property updates now
				Values[zeroIndex][0] = 0;
				double inventory = myIntegrator.getSumVolume();
				lastInventory = inventory;
				double multipliedInventory = inventory * multiplier;
				Values[inventoryIndex][0] = multipliedInventory;
				if (Math.Abs(multipliedInventory) < 30) return; // minimum inventory required !
				// place DOTs against Price indicating possible RISK
				{
					double tickSize = this.TickSize; // price increment of 1 price tick
					double midPrice = ( 0.5 * ( lastBidPrice+lastAskPrice ));
					if (wasSuperSpikeBuy) {
						wasSuperSpikeBuy = false;
						Draw.Dot( this, getShortTag(), true, 0, (midPrice+2*tickSize), 
									superSpikeBuyBrush);
					}
					if (wasSuperSpikeSell) {
						wasSuperSpikeSell = false;
						Draw.Dot( this, getLongTag(), true, 0, (midPrice-2*tickSize), 
									superSpikeSellBrush);
					}
					//double riskTicks = 0;
					if (multipliedInventory>0) { // positive inventory "Short Risk"
						double vwapPositive = 
							myIntegrator.getVwapPrice(VolumePriceExpiryIntegrator.VWAP_POSITIVE_VOLUMES);
						if ( vwapPositive != 0 ) { // valid; a zero price is INVALID
							double deltaTicks = (midPrice - vwapPositive) / tickSize;
							if (DBG) {
								ninjaPrint2("vwapPositive price: "+vwapPositive);
							}
							if (deltaTicks>=riskThresholdTicks) {
								if (deltaTicks>30) deltaTicks=30;
								Draw.Dot( this, getShortTag(), true, 0, (midPrice+4+(0.25*deltaTicks)), 
									AltColor?shortRiskBrushAlt:shortRiskBrush);
							}
						}
					}
					else { // negative inventory "Long Risk"
						double vwapNegative = 
							myIntegrator.getVwapPrice(VolumePriceExpiryIntegrator.VWAP_NEGATIVE_VOLUMES);
						if (vwapNegative != 0) { // valid; a zero price is INVALID
							double deltaTicks = ( vwapNegative - midPrice ) / tickSize;
							if (DBG) {
								ninjaPrint2("       vwapNegative price: "+vwapNegative);
							}
							if (deltaTicks>=riskThresholdTicks) {
								if (deltaTicks>30) deltaTicks=30;
								Draw.Dot( this, getLongTag(), true, 0, (midPrice-4 - (0.25*deltaTicks)), 
									AltColor?longRiskBrushAlt:longRiskBrush);
							}
						}
					}
				}
			}
		}

		#region periodic SimplePropertyReader
	
	private static bool DBG_KV = false; // debug only
    protected class SimplePropertyReader {

        // USAGE: Create object
        // setPropertiesFileNameDotTxt
        //   use name "." (default directory) \\ double backslash
        //   desiredName.txt
        // if file exists, then 
        //   the given properties will be ignored, and the
        //      properties dictionary contents taken from the file
        //   otherwise, the given properties are written to
        //   a newly created properties file
        // setPropertiesSamplingSecs
        // call start() once only
        // on termination, call stop()
		TradeFlowRisk parent = null;
        public SimplePropertyReader(TradeFlowRisk parentArg) {
			parent = parentArg;
            // client should set all properties, prior to start()
        }

        // please call start only once
        public void start() {
            ThreadStart ts = new ThreadStart(propertiesReaderLoop);
            Thread runner = new Thread(ts);
            isRunning = true;
            runner.Start();
        }

        private string fileName = "myProperties.txt";

        // if used, must do this BEFORE start()
        public void setPropertiesFileNameDotTxt(string fileNameArg) {
            fileName = fileNameArg;
        }

        // if used, do this BEFORE calling start();
        public void setPropertiesSamplingSecs(int secondsArg) {
            propertiesSampleDelayMsecs = secondsArg * 1000;
        }

        private bool isRunning = false;
        private int propertiesSampleDelayMsecs = 30000;

        public void stop() {
            isRunning = false;
            notifyUpdate(); // break thread wait; and exit
        }
        private object threadWaitObject = new object();
        private bool isNotifiedLocked = false;
        public void notifyUpdate() {
            lock(threadWaitObject) {
                    isNotifiedLocked = true;
                    Monitor.Pulse(threadWaitObject);
            }
        }

        private void propertiesReaderLoop() {
            try {
				parent.ninjaPrint("propertiesReaderLoop started");
                while (isRunning) {
                    try {
                        lock (threadWaitObject) {
                            if (!isNotifiedLocked) {
                                Monitor.Wait(threadWaitObject, propertiesSampleDelayMsecs, true); // every XX secs or sooner
                                isNotifiedLocked = false;
                            }
                            if (!isRunning) continue; // thread will stop
                                                      // read property file
                            if ( !updateFromPropertiesFile()) continue; // error in file or first time
                            lock(parent.getUpdateMutex()) {
                                  parent.updatePropertiesCallback();
                            }
                        }
                    }
                    catch(Exception ex) {

                    }
                }
            }
            catch(Exception ex) {
                // nothing
            }
        }

        // client must set ALL properties and values BEFORE start()
        public void setKeyValue(string keyArg, string value) {
            lock (propertiesDictionary) {
                string key = keyArg.ToUpper().Trim();
                string strValue = "" + value;
                if (propertiesDictionary.ContainsKey(key)) {
                    propertiesDictionary[key] = strValue;
                } else {
                    propertiesDictionary.Add(key, strValue);
                }
            }
        }

        private Dictionary<string, string> propertiesDictionary = new Dictionary<string, string>();

        public int getIntegerProperty( string key) {
            return (int)getDoubleProperty(key);
        }

        public double getDoubleProperty(string keyArg) {
            string key = keyArg.ToUpper().Trim();
			double retDbl = 0;
            lock (propertiesDictionary) {
                if (propertiesDictionary.ContainsKey(key)) {
					try {
						retDbl = Double.Parse(propertiesDictionary[key]);
					} catch(Exception ex) { throw; } // propogate exception to caller
				}
            }
			return retDbl;
        }

        public bool getBooleanProperty(string keyArg) {
            string key = keyArg.ToUpper().Trim();
            bool retBool = false;
            lock (propertiesDictionary) {
                if (propertiesDictionary.ContainsKey(key)) {
                    try {
                        retBool = Boolean.Parse(propertiesDictionary[key].Trim().ToLower());
                    } catch (Exception ex) { throw; } // propogate exception to caller
                }
            }
            return retBool;
        }

        private bool updateFromPropertiesFile() {
			parent.ninjaPrint("updateFromPropertiesFile called... ");
            // if the properties file doesn't exist, then we create one
            // and populate with the provided properties in form xxx=yyy
			bool successful = true;
            string fullFilePath = parent.customRootPath + fileName;
            if ( !File.Exists(fullFilePath)) { // default dir; backslash is special escape char
				try {
                // write a new one
				//parent.ninjaPrint("updateFromPropertiesFile new file "+fullFilePath);
                using (StreamWriter outputFile = new StreamWriter(fullFilePath)) {
                    outputFile.WriteLine("# comment line");
                    lock (propertiesDictionary) {
                        foreach (var item in propertiesDictionary) {
                            string key = item.Key;
                            string value = item.Value;
                            outputFile.WriteLine(key + "=" + value);
                        }
                        outputFile.Close();
                    }
                }
				} catch(Exception ex) { successful = false;
					parent.ninjaPrint("Exception creating file: "+ex.Message);}
                return successful;
            }
            // existing file, need to re-read and update our properties dictionary
            if (File.Exists(fullFilePath)) { // should definitely exist !!!
                                              // write a new one
				try {
                using (StreamReader inputFile = new StreamReader(fullFilePath)) {
                    lock (propertiesDictionary) {
                        //propertiesDictionary.Clear();
                        string line = " ";
                        while ((line = inputFile.ReadLine()) != null) {
							if (line.StartsWith("#")) continue; // skip comment
                            string trimmedLine = line.Trim();
                            if (trimmedLine.Length == 0) continue;
                            if (!trimmedLine.Contains("=")) return false;
                            string[] splitString = line.Split('=');
                            string key = splitString[0].ToUpper().Trim();
                            string value = splitString[1].ToLower().Trim(); // e.g. FALSE to false
							if (DBG_KV) {
								parent.ninjaPrint("Key: "+key+" value: "+value);
							}
							if ( !propertiesDictionary.ContainsKey(key) ) {
                            	propertiesDictionary.Add(key, value);
							}
							else {
								propertiesDictionary[key] = value;
							}
                        }
                        inputFile.Close();
                    }
                }
				} catch(Exception ex2) { successful = false; 
					parent.ninjaPrint("Exception: "+ex2.Message);}
            }
			return successful;
        }
    } // end class

		#endregion

	#region Simple Rate Limiter
    class SimpleRateLimiter {

        protected static Random randomizer = new Random();

        int msecs = 10; // default

        bool useRandom = false; // slightly randomize interval which
        // would untentionally de-synchronize any large collection of RateLimiters

        public void setUseRandom(bool flag) {
            useRandom = flag;
            if (!useRandom) deltaMsecsRandom = 0;
        }

        private DateTime lastUpdate = DateTime.UtcNow;

        public SimpleRateLimiter(int msecsArg) {
            msecs = msecsArg;
        }

        // randomly return -1..+1
        protected static int nextDelta() {
            lock (randomizer) {
                double ran = randomizer.NextDouble();
                if (ran < 0.3333) {
                    return -1;
                } else
                if (ran < 0.6666) {
                    return 0;
                }
                return 1;
            }
        }

        public void setMsecsDelay(int msecsArg) {
            int _msecs = msecsArg;
            if (_msecs < 1) _msecs = 1;
            msecs = _msecs;
        }

        private int deltaMsecsRandom = 0;

        public bool allowUpdate() {
            DateTime now = DateTime.UtcNow;
            TimeSpan elapsed = (now - lastUpdate);
            if (elapsed.TotalMilliseconds < msecs + deltaMsecsRandom) return false;
            lastUpdate = now;
            if (useRandom) deltaMsecsRandom = nextDelta();
            return true;
        }

    }

	#endregion
	
	}
}

#region NinjaScript generated code. Neither change nor remove.

namespace NinjaTrader.NinjaScript.Indicators
{
	public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
	{
		private TradeFlowRisk[] cacheTradeFlowRisk;
		public TradeFlowRisk TradeFlowRisk(string fileName, bool altColor)
		{
			return TradeFlowRisk(Input, fileName, altColor);
		}

		public TradeFlowRisk TradeFlowRisk(ISeries<double> input, string fileName, bool altColor)
		{
			if (cacheTradeFlowRisk != null)
				for (int idx = 0; idx < cacheTradeFlowRisk.Length; idx++)
					if (cacheTradeFlowRisk[idx] != null && cacheTradeFlowRisk[idx].FileName == fileName && cacheTradeFlowRisk[idx].AltColor == altColor && cacheTradeFlowRisk[idx].EqualsInput(input))
						return cacheTradeFlowRisk[idx];
			return CacheIndicator<TradeFlowRisk>(new TradeFlowRisk(){ FileName = fileName, AltColor = altColor }, input, ref cacheTradeFlowRisk);
		}
	}
}

namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
	public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
	{
		public Indicators.TradeFlowRisk TradeFlowRisk(string fileName, bool altColor)
		{
			return indicator.TradeFlowRisk(Input, fileName, altColor);
		}

		public Indicators.TradeFlowRisk TradeFlowRisk(ISeries<double> input , string fileName, bool altColor)
		{
			return indicator.TradeFlowRisk(input, fileName, altColor);
		}
	}
}

namespace NinjaTrader.NinjaScript.Strategies
{
	public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
	{
		public Indicators.TradeFlowRisk TradeFlowRisk(string fileName, bool altColor)
		{
			return indicator.TradeFlowRisk(Input, fileName, altColor);
		}

		public Indicators.TradeFlowRisk TradeFlowRisk(ISeries<double> input , string fileName, bool altColor)
		{
			return indicator.TradeFlowRisk(input, fileName, altColor);
		}
	}
}

#endregion

hyperscalper

Attached Thumbnails
Click image for larger version

Name:	Trade-flow-risk-overlain.PNG
Views:	157
Size:	215.9 KB
ID:	318931  
Started this thread Reply With Quote
Thanked by:
  #87 (permalink)
 hyperscalper 
boise idaho
 
Experience: Advanced
Platform: NinjaTrader C# Custom
Broker: NinjaTrader LeeLoo Rithmic
Trading: Nasdaq Futures NQ/MNQ
Posts: 314 since Apr 2020
Thanks Given: 15
Thanks Received: 522

NASDAQ "DIRTY TRICKS" AND TRADE FLOW ANALYSIS

By way of emphasizing that Trade Flow is a "situational variable"
and also understanding that External factors will drive Market
Maker into Risk, without necessarily turning the market...

Attached is a screenshot where the Nasdaq shows an "irrational"
persistent (but false) breakout into Short Risk which can
risk a Trader's shirt and trousers.

This is a Friday; and on Fridays all sorts of "shenanigans" and
"dirty tricks" generally appear.

This is why I have chosen the Nasdaq futures contract as my
only target Instrument. Because it is "hands down" the most
difficult environment in which to consistently profit.

So, in posting this, it appears that my argument for the usefulness
of TradeFlowRisk as an Indicator, is revealed as weak or
potentially useless...

But, as I said earlier, "external" factors can push beyond where
a natural turning point should exist; and Trade Flow Analysis
should be considered a "situational awareness indicator".

After a 20-30 point Breakout, it was revealed as False; and fell
back. But unless you are a very Smart trader; this can take
you down...

hyperscalper

Attached Thumbnails
Click image for larger version

Name:	Nasdaq-irrational-breakout.PNG
Views:	115
Size:	230.6 KB
ID:	318951  
Started this thread Reply With Quote
  #88 (permalink)
 SpeculatorSeth   is a Vendor
 
Posts: 780 since Apr 2016
Thanks Given: 22
Thanks Received: 1,018


justtrader View Post
@hyperscalper
I wonder if you can modify the code to access the properties file only after it gets modified. And/Or perhaps check for file re-written with your timer and if true then read it. Most of the time the properties file will not change for a long time. Hence why read it all the time (save some CPU energy).

Some pseudo code:
if (lastModifiedTime != File.GetLastWriteTimeUtc) {
ReadFile();
lastModifiedTime = File.GetLastWriteTimeUtc;
}

As I said before it would be extremely beneficial to just remove the properties file altogether and store information in standard [NinjaScriptProperty] getters and setters. Then NinjaTrader handles all of that stuff for you, and you can take full advantage of NinjaTrader's features. Having an external file will break things like the Strategy Builder, AI Generate, or optimization backtesting.

Since they're just properties, the code can modify them at any time. If you want to modify the properties yourself you just open up the indicator properties. Reading and writing to file only happens when it needs to for saving the workspace. You'll really have an easier time if you work with the way NinjaTrader does things rather than reinventing the wheel.

- SpeculatorSeth
Reply With Quote
  #89 (permalink)
 hyperscalper 
boise idaho
 
Experience: Advanced
Platform: NinjaTrader C# Custom
Broker: NinjaTrader LeeLoo Rithmic
Trading: Nasdaq Futures NQ/MNQ
Posts: 314 since Apr 2020
Thanks Given: 15
Thanks Received: 522


TWDsje View Post
As I said before it would be extremely beneficial to just remove the properties file altogether and store information in standard [NinjaScriptProperty] getters and setters. Then NinjaTrader handles all of that stuff for you, and you can take full advantage of NinjaTrader's features. Having an external file will break things like the Strategy Builder, AI Generate, or optimization backtesting.

Since they're just properties, the code can modify them at any time. If you want to modify the properties yourself you just open up the indicator properties. Reading and writing to file only happens when it needs to for saving the workspace. You'll really have an easier time if you work with the way NinjaTrader does things rather than reinventing the wheel.

Seth, sure everything you said is clear to me and others.

My work is Real Time only; therefore, I don't want to lose all
of the Real Time data accumulation; so I want to change key
properties, "on the fly" while the Indicator is running.

When you do it "the NinjaTrader properties way", of course
you have to scan through Historical, and then possibly flip
into Real Time every time you change a Property value; I think
that's how it works... and that creates huge problems with
complex Indicators which have significant "state"... Just sayin'...

I think I made that very clear; and my reasons are that much of
the information I need is NOT AVAILABLE from historical replay.
Not so much in the case of this indicator, but certainly for Depth
of Market replay, which is simply NOT POSSIBLE for the
events and analyses that I typically am looking at.....

The "NinjaScript way" is really great for "simple" indicators; but
when things get very critical and complex; in my experience, that
framework is just not good enough.

But, it's good enough for most traders; so I'd just count myself
as an exception, and one key factor for me, is that I don't do
Backtesting... Not that there's anything wrong with those of
you who *do* want to do Backtesting... I just have a different approach...

NinjaTrader could easily allow for Property changes; without resetting the Indicator
I'm pretty sure.... But they don't do that, AFAIK; maybe I'm wrong?...

[EDIT] I doubt that more than 0.1% of NinjaScript Indicator writers are aware that
multiple "object clones" are created on Indicator query and startup; and that's also
true for Strategy instances. These "clones" are distinct Object instances, and most
Indicator writers don't need to know that this cloning is happening; so long as they
keep things "simple" and also "stateless" as much as possible.

hyperscalper

Started this thread Reply With Quote
  #90 (permalink)
 SpeculatorSeth   is a Vendor
 
Posts: 780 since Apr 2016
Thanks Given: 22
Thanks Received: 1,018



hyperscalper View Post
My work is Real Time only; therefore, I don't want to lose all
of the Real Time data accumulation; so I want to change key
properties, "on the fly" while the Indicator is running.

When you do it "the NinjaTrader properties way", of course
you have to scan through Historical, and then possibly flip
into Real Time every time you change a Property value; I think
that's how it works... and that creates huge problems with
complex Indicators which have significant "state"... Just sayin'...

Properties can also be changed by code. If you really want to get fancy you can take advantage of some of C#'s unique features to add GUI elements to the chart, or create an pop out window for it.

Also, this little bit of code is useful outside of just the OnStateChanged method:

 
Code
if (State == State.Historical)
{
}

Not that tick replay ends up being all that different from live data except perhaps for 1 second timeframes. However, this is really useful because it enables the next solution...


Quoting 
I think I made that very clear; and my reasons are that much of
the information I need is NOT AVAILABLE from historical replay.
Not so much in the case of this indicator, but certainly for Depth
of Market replay, which is simply NOT POSSIBLE for the
events and analyses that I typically am looking at.....

Anything can be saved to a file and recalled historically. Then you can do real a real empirical study about what your changes are actually doing. You won't be changing things on the fly by hand very much either because you'll already know the optimal value for the situation.

But more importantly then other people can more effectively use the work to develop it further. It will play nice with other indicators, and you can use them in strategies.

- SpeculatorSeth
Reply With Quote




Last Updated on January 26, 2023


© 2024 NexusFi™, s.a., All Rights Reserved.
Av Ricardo J. Alfaro, Century Tower, Panama City, Panama, Ph: +507 833-9432 (Panama and Intl), +1 888-312-3001 (USA and Canada)
All information is for educational use only and is not investment advice. There is a substantial risk of loss in trading commodity futures, stocks, options and foreign exchange products. Past performance is not indicative of future results.
About Us - Contact Us - Site Rules, Acceptable Use, and Terms and Conditions - Privacy Policy - Downloads - Top
no new posts