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 46,088 views
    2. thumb_up 328 thanks given
    3. group 55 followers
    1. forum 248 posts
    2. attach_file 80 attachments




 
 

Design a DayTrader Scalping Order Flow Indicator

 
 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

LET'S DISCUSS THE KEY RATIONALE AND APPROACH FOR
ORDER FLOW PROCESSING, WHICH WILL YIELD FREQUENT LOCAL
TOPS AND BOTTOMS FOR A DAYTRADER SCALPER.

In order to do this, we'll discuss the nature of trading; a theory of
what should be observed at relative local tops (to Sell) and bottoms
(to Buy) through analysis of the Time and Sales.

The main focus is on a Futures contract such as the Nasdaq 100 (NQ)
contract, and eventually use code snippets and an Indicator sample
framework, which can be adapted for NinjaTrader 8, and implemented
in C#.

There are many commercially available Order Flow Indicators you can
purchase; but this is oriented around Designing and Implementing
one that meets specific requirements and goals for yielding
useful Tradable Signals.

I'll try to move through the Theory, Design and Implementation details
so that you can see what we are trying to measure, how, and why.

If you like to criticize, and just like to punch holes in the fundamental
assumptions or usefulness of this task, consider just going elsewhere.

Anybody can invent reasons why "It just won't work" or "Order Flow Analysis"
is just a junk indicator. If that's your attitude, you don't need to read
any of this.

We will distinguish between "Order Flow" and "Trade Flow"; and what we
will be designing is really "Trade Flow Analysis". The difference is that
real "Order Flow Analysis" requires to much more challenging area of
analyzing the Live Depth of Market, which is way beyond the scope...

So we'll be talking about Trade Flow Analysis; which represents Orders
(Bids and Offers on the Book) which have resulted in Retail Trade Executions.
And the goal is to find the "imbalances" which occur in "inventory" which
changes hands between Retail players, and Market Makers.

This is a challenge which is within the scope of the relatively small
and contained NinjaScript environment; but preference would always
be that C# coders use Visual Studio (Community Edition) for higher
levels of productivity.

CONCEPTS OR SNIPPETS ARE "AS IS" AND USEFULNESS IS NOT
GUARANTEED, although I wouldn't be proposing them if I didn't hope
that Traders could benefit.

hyperscalper

Started this thread

Can you help answer these questions
from other members on NexusFi?
Exit Strategy
NinjaTrader
My NT8 Volume Profile Split by Asian/Euro/Open
NinjaTrader
Online prop firm The Funded Trader (TFT) going under?
Traders Hideout
NexusFi Journal Challenge - April 2024
Feedback and Announcements
Better Renko Gaps
The Elite Circle
 
Best Threads (Most Thanked)
in the last 7 days on NexusFi
Get funded firms 2023/2024 - Any recommendations or word …
59 thanks
Funded Trader platforms
37 thanks
NexusFi site changelog and issues/problem reporting
24 thanks
GFIs1 1 DAX trade per day journal
22 thanks
The Program
19 thanks
 
 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

WHY SHOULD TRADE FLOW ANALYSIS GIVE YOU AN EDGE?

Trading is fundamentally a battle between Buyers and Sellers.

As a Retail Trader, you Buy and you Sell from/to a Market Maker.

You Buy at the lowest Offer/Ask price; and you Sell at the highest
Bid price. Yes, you do.

Now, there are those of you who will immediately point out that
a retail trader can place a Limit order to Buy below the market,
and when the market comes down; that lucky guy will be able
to Buy at the Bid. Correspondingly, a Limit to Sell above the
market has the opportunity to Sell at the Offer/Ask price.

In those situations, it would SEEM that the Retail Trader enjoys
the advantages of Market Maker, who ALWAYS "makes the spread"
by Buying (from Retail Sellers) at the BID, and Selling
(to Retail Buyers) at the ASK price, thus "making the spread".

Retail players using Market Orders obviously "pay the spread"
or are sometimes called "takers" of liquidity, as opposed to
"makers" which is what a Market Maker does, by definition.

Anyway, the point is that without Market Maker, markets would
be rather "thin" in liquidity since Retail participants would have to
trade amongst themselves, and liquidity would be very shallow.

With Market Maker, liquidity is relatively Deep and Pricing is also
relatively stable. You are Billy the Buyer, and you do not need
your counterpart, Sally the Seller, in order for you to BUY a
futures contract, or any "inventory".

INVENTORY is a concept which describes what Market Maker has
Bought or Sold, while interacting with the Retail population of
Buyers and Sellers.

THE GOAL of Trade Flow Analysis is to determine when Market
Maker may have an Imbalance in Inventory. For example, on a
declining Price, most Retail players are Sellers.

Yes, I can see you saying "But some are also Buyers" yes, but
we can ignore that observation, since I claim it is a relatively
unimportant fact; and that the dominant behavior is that Retail
players are Sellers on a falling Price (from whom Market Maker
BUYs INVENTORY).

When MM Buys Inventory, at some point, she (MM is represented
as female in this theory) has Bought lots of "contracts" but has
not yet been able to Sell them (at a higher price) and so we can
say a couple of things about MM in this situation:

1) over a recent timeframe, MM has bought much more inventory
than she has been able to sell. In our terminology, we will say
that MM is "Long" or has "Long Inventory".

2) The Volume Weighted Average Price of that "Long Inventory"
is actually higher, and so at the moment Price stops falling, we
can say that MM is "Losing Money on Paper" on that "Long Inventory"
which has been bought from Retail Sellers.

3) When MM is "underwater" against the VWAP of Inventory; we say
that she is "at risk" or in this case "In Long Risk".

STOP FOR A MOMENT and satisfy yourself that you are understanding
these dynamics, and start imagining how our knowledge of that
situation can function as a BUY signal, which may be associated with
Price halting its downward move; and potentially beginning to rise.

BUT, I can hear some of you say "But what Forces cause Price to move"
and many of you will say things like "The Price is Falling, because Retail
Sellers are 'in control'" (my favorite phrase, which is a Traders' Myth).

WHY DO PRICES MOVE? Is it because "Retail players are in a Selling
mood, and drive prices down?" or is it because Market Maker wants
price to move down, so "MM is in control of all Price movements"??

CUTTING TO THE CHASE, we presume that MM is in control of ALL
price moves, and that MM is using the Retail population as a counter
party, in order to achieve her Accumulation and Distribution of
Inventory.

Maybe you already knew all of that and, if so, Congratulations, because
you have a deeper understanding of Market Dynamics, which is
embedded deep in the nature of all Trading.

hyperscalper

Started this thread
 
 
justtrader's Avatar
 justtrader 
San Francisco, CA
 
Experience: Intermediate
Platform: Ninja Trader, TOS
Trading: es, rty, cl, gc, nq, ym
Posts: 182 since May 2011
Thanks Given: 104
Thanks Received: 173


Thanks for the lecture.


hyperscalper View Post
WHY SHOULD TRADE FLOW ANALYSIS GIVE YOU AN EDGE?

Trading is fundamentally a battle between Buyers and Sellers.

As a Retail Trader, you Buy and you Sell from/to a Market Maker.

You Buy at the lowest Offer/Ask price; and you Sell at the highest
Bid price. Yes, you do.

Now, there are those of you who will immediately point out that
a retail trader can place a Limit order to Buy below the market,
and when the market comes down; that lucky guy will be able
to Buy at the Bid. Correspondingly, a Limit to Sell above the
market has the opportunity to Sell at the Offer/Ask price.

In those situations, it would SEEM that the Retail Trader enjoys
the advantages of Market Maker, who ALWAYS "makes the spread"
by Buying (from Retail Sellers) at the BID, and Selling
(to Retail Buyers) at the ASK price, thus "making the spread".

Retail players using Market Orders obviously "pay the spread"
or are sometimes called "takers" of liquidity, as opposed to
"makers" which is what a Market Maker does, by definition.

Anyway, the point is that without Market Maker, markets would
be rather "thin" in liquidity since Retail participants would have to
trade amongst themselves, and liquidity would be very shallow.

With Market Maker, liquidity is relatively Deep and Pricing is also
relatively stable. You are Billy the Buyer, and you do not need
your counterpart, Sally the Seller, in order for you to BUY a
futures contract, or any "inventory".

INVENTORY is a concept which describes what Market Maker has
Bought or Sold, while interacting with the Retail population of
Buyers and Sellers.

THE GOAL of Trade Flow Analysis is to determine when Market
Maker may have an Imbalance in Inventory. For example, on a
declining Price, most Retail players are Sellers.

Yes, I can see you saying "But some are also Buyers" yes, but
we can ignore that observation, since I claim it is a relatively
unimportant fact; and that the dominant behavior is that Retail
players are Sellers on a falling Price (from whom Market Maker
BUYs INVENTORY).

When MM Buys Inventory, at some point, she (MM is represented
as female in this theory) has Bought lots of "contracts" but has
not yet been able to Sell them (at a higher price) and so we can
say a couple of things about MM in this situation:

1) over a recent timeframe, MM has bought much more inventory
than she has been able to sell. In our terminology, we will say
that MM is "Long" or has "Long Inventory".

2) The Volume Weighted Average Price of that "Long Inventory"
is actually higher, and so at the moment Price stops falling, we
can say that MM is "Losing Money on Paper" on that "Long Inventory"
which has been bought from Retail Sellers.

3) When MM is "underwater" against the VWAP of Inventory; we say
that she is "at risk" or in this case "In Long Risk".

STOP FOR A MOMENT and satisfy yourself that you are understanding
these dynamics, and start imagining how our knowledge of that
situation can function as a BUY signal, which may be associated with
Price halting its downward move; and potentially beginning to rise.

.........

hyperscalper

Quote: "our knowledge of that
situation can function a BUY signal, which may be associated with
Price halting its downward move; and potentially beginning to rise."

I agree. However, there may be many, many, times when a drop halts to then continue down. Hence the real question is how to determine a "real" halt from a "false" halt?

JT

TWYS NWYT (Price Advertises Opportunity; Time Regulates it; Volume Measures its Success/Failure ---- Dalton)
 
 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
Thanks for the lecture.



Quote: "our knowledge of that
situation can function a BUY signal, which may be associated with
Price halting its downward move; and potentially beginning to rise."

I agree. However, there may be many, many, times when a drop halts to then continue down. Hence the real question is how to determine a "real" halt from a "false" halt?

JT

Yes, the answer to THAT problem is far beyond Trade Flow Analysis, but
even absent tackling of that issue, there is a High level of signal validity
which emerges from Trade Flow.

The only way to go beyond that; where I've gone in my work, but am not
going to try and explain... is to tackle the 20x more difficult problem of
Market Depth Analysis which, technically would be "Order Flow or Pattern
Analysis" of the DOM. Yes, there are solutions there, but I can't go there
due to the hugely more complex nature of that problem.

Your instincts are correct, and there are answers; but one thing at a
time that could be achievable by a coding-competent trader.

hyperx

Started this thread
 
 
justtrader's Avatar
 justtrader 
San Francisco, CA
 
Experience: Intermediate
Platform: Ninja Trader, TOS
Trading: es, rty, cl, gc, nq, ym
Posts: 182 since May 2011
Thanks Given: 104
Thanks Received: 173


hyperscalper View Post
Yes, the answer to THAT problem is far beyond Trade Flow Analysis, but
even absent tackling of that issue, there is a High level of signal validity
which emerges from Trade Flow.

The only way to go beyond that; where I've gone in my work, but am not
going to try and explain... is to tackle the 20x more difficult problem of
Market Depth Analysis which, technically would be "Order Flow or Pattern
Analysis" of the DOM. Yes, there are solutions there, but I can't go there
due to the hugely more complex nature of that problem.

Your instincts are correct, and there are answers; but one thing at a
time that could be achievable by a coding-competent trader.

hyperx

I do not consider myself a sophisticated coder but I have an adequate grasp of C#. I coded on NT7, years ago, my own version of the foot print, but I could not readily see any real value and abandon it.

Hopefully, with your assistance I might see value and start from scratch on NT8.

Thanks,

JT

TWYS NWYT (Price Advertises Opportunity; Time Regulates it; Volume Measures its Success/Failure ---- Dalton)
Thanked by:
 
 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

MORE CONCEPTS WILL BE INTRODUCED, BUT
I'm sure you all, as much as myself, want to start jumping
into "code snippets" which represent the major concepts
in a form which could be placed within a NinjaTrader 8
NinjaScript Indicator.

(We will NOT be developing a Strategy, which could have the
capability of placing Orders based on an Indicator. That just
introduces many other factors, that we'll be ignoring. But if
you're good at Strategies, then perhaps you'll want to embed
the processing within one. But I'd recommend "keeping it simple"
and writing an Indicator. About Indicators, I've never used one
as an "embed" within another Indicator or Strategy, so I don't
really know about that mechanism...)

So, over the weekend, I'll try to deliver SOME of the concepts, as
well as some sample code snippets to accomplish them.

WHAT IS THE ANALYSIS PIPELINE GOING TO BE LIKE ?

1) CATEGORIZE TRADES AS RETAIL SELLING, OR RETAIL BUYING,
and by convention we'll just represent Volume as negative if the
trade is to the BID (Retail Sell) and positive if the trade is to
the ASK/OFFER (Retail Buy). These Volumes in the NQ futures
are going to be INTEGERS (int).

2) PERFORM SOME DATA COMPRESSION ON THE RAW SIGNED VOL,
so we'll have a snippet to do that. Maybe feed this into an optional
Smoother, but that's probably not needed.

3) PROVIDE AN INTEGRATOR INTO WHICH WE'LL FEED THE
COMPRESSED VOLUMES, which will not be "int" anymore, but
will be "double" values, again, signed for Buy (+) vs Sell (-) trade.
By convention we'll take the Retail Sell perspective as negative
but from Market Maker's perspective, it is a Buy, of course.
The Expiration Time Window of the Integration will be able to
be adjusted "on the fly" by our file properties mechanism below.

4) PROVIDE SUPPORT FOR THE CONCEPT OF A "BIG LOT", which
means we'll have a Minimum Value for Size that we'll consider,
and typically we'll ignore Single Contract Trades. This will support
the concept of "Big Lots". This is a key concept.

5) DISCUSS THE INTEGRATOR'S BEHAVIOR, with implementation.
The Integrator needs to be a) fast b) needs to provide for
a TIME WINDOW of EXPIRATION, and c) needs to provide for
TAPERING toward zero, of elements which are EXPIRING or leaving
the active time window, and d) of course, provide the SUM
or the integration of all "Big Lot Trades" (signed, and compressed)
which lie within the current Integrator expiration MOVING
Time Window..... whew !!!

5a) WE WILL DO REAL TIME ONLY. So we will not be activating
the indicator until the RealTime state is reached on initialization...

6) A USER INTERFACE FOR AN iNDICATOR IS NOT PROVIDED
by the NinjaTrader facility, AFAIK. Parameters are fed in ONLY on the
Start of the Indicator, and cannot (easily) be changed WHILE
the Indicator is running; and we'll need to be able to do that,
so we'll just have the Parameters reside in a text file, which is
periodically read by the Indicator; which can be held in an
Editor (such as Notepad++) edited "on the fly" and updates
will be periodically read, and updated WITHIN the running
Indicator, e.g. changing the number of Seconds wide for the
Integration interval.

7) AND FINALLY, IN THE OnBarUpdate of the Indicator, we will
Plot a line which represents the Current Inventory Time Window's
summation, or integration (with Aging/Expiring Trade items
being gradually Tapered toward the trailing edge of the active
integration window to smooth
things out).

I can hear that this all seems obvious, and if you're an Indicator Guy,
then you know all this, but "The Devil is in the Details" and the
fidelity of the algorithms in capturing the Essential Concepts
we're trying to measure as Signal components.

Maybe I missed out some steps or considerations here, but they'll
be filled in as we proceed.

hyperscalper

Started this thread
 
 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

CODING STYLE...
you're not gonna like this ! LOL

I'm a Java coder, and I think of C# as a "ripoff" of Java; but the
truth is that both descend from the C language. But, fortunately
for me, I can write C# just like I write Java, so that's what
I do.... not gonna debate it

I'm gonna use Java conventions for Braces {} and also for
Variable declarations (first char is lowerCase) and also I'm
not typically gonna use C# Properties, but rather getXXX()
style.

Visual Studio can be configure to be more "friendly" to one or
the other styles, so you don't have to "fight" the editor's formatting
too much...

These two styles are illustrated like this:

 
Code
if (you.hasAnswer()) { // Java braces type convention
    you.postAnswer();
} else {
    you.doSomething();
}
or... C#

 
Code
if (you.hasAnswer())
{
    you.postAnswer();
}
else
{
    you.doSomething();
}
You're obviously free (and encouraged) to use your own style, but
don't expect ME to like the C# style any more/less (?) than YOU may
like/dislike the Java style )

hyperscalper

Started this thread
 
 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

OK, I CAN SENSE YOUR EXCITEMENT SO HERE'S SOME STARTER
CODE. I quickly hacked this together...

If there are Bugs, please be kind to my fragile Ego...

Should be able to paste it in the NinjaScript editor; which I find very
annoying, but it's the easiest way, so long as things don't get too
complicated...

 
Code
#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
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
{
	public class TradeFlowTutorial : Indicator
	{
		protected override void OnStateChange()
		{
			if (State == State.SetDefaults)
			{
				Description									= @"in Futures.IO an exercise in developing a simple Trade Flow analyzer.";
				Name										= "TradeFlowTutorial";
				Calculate									= Calculate.OnEachTick;
				IsOverlay									= false;
				DisplayInDataBox							= false;
				DrawOnPricePanel							= false;
				DrawHorizontalGridLines						= true;
				DrawVerticalGridLines						= true;
				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					= true;
			}
			else if (State == State.Configure)
			{
				init();
			}
			else if (State == State.Realtime)
			{
			}
			else if (State == State.Terminated)
			{
				deInit();
			}
		}
		
		private void init() {
		}
		
		private static bool USE_NINJA_PRINT = true;
		
        private void ninjaPrint(string msg) { // primary tab
            if (!USE_NINJA_PRINT) return;
            PrintTo = PrintTo.OutputTab1;
            Print(msg);
        }

        private void ninjaPrint2(string msg) { // secondary tab
            if (!USE_NINJA_PRINT) return;
            PrintTo = PrintTo.OutputTab2;
            Print(msg);
        }

        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
        }



		private void deInit() {
		}
		
		private static bool LOG_RAW_VOLUME = true;
		
		private void reportTrade(int tradeRawVol, double price) {
			if ( LOG_RAW_VOLUME ) {
				if (tradeRawVol>0) {
					ninjaPrint("Retail Buy "+tradeRawVol+" @"+price);
				}
				else {
					ninjaPrint("       Retail Sell "+tradeRawVol+" @"+price);
				}
			}
			double compressedVol = compressSizeMinOneSigned( tradeRawVol );
			reportToIntegrator( compressedVol );
		}
		
		private void reportToIntegrator(double compressedVolArg) {
		}
		
		private double lastBidPrice = 0;
		private double lastAskPrice = 0;

		protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
		{
			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
			}
			return;
		}

		protected override void OnBarUpdate()
		{
			//Add your custom indicator logic here.
		}
	}
}

#region NinjaScript generated code. Neither change nor remove.

namespace NinjaTrader.NinjaScript.Indicators
{
	public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
	{
		private TradeFlowTutorial[] cacheTradeFlowTutorial;
		public TradeFlowTutorial TradeFlowTutorial()
		{
			return TradeFlowTutorial(Input);
		}

		public TradeFlowTutorial TradeFlowTutorial(ISeries<double> input)
		{
			if (cacheTradeFlowTutorial != null)
				for (int idx = 0; idx < cacheTradeFlowTutorial.Length; idx++)
					if (cacheTradeFlowTutorial[idx] != null &&  cacheTradeFlowTutorial[idx].EqualsInput(input))
						return cacheTradeFlowTutorial[idx];
			return CacheIndicator<TradeFlowTutorial>(new TradeFlowTutorial(), input, ref cacheTradeFlowTutorial);
		}
	}
}

namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
	public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
	{
		public Indicators.TradeFlowTutorial TradeFlowTutorial()
		{
			return indicator.TradeFlowTutorial(Input);
		}

		public Indicators.TradeFlowTutorial TradeFlowTutorial(ISeries<double> input )
		{
			return indicator.TradeFlowTutorial(input);
		}
	}
}

namespace NinjaTrader.NinjaScript.Strategies
{
	public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
	{
		public Indicators.TradeFlowTutorial TradeFlowTutorial()
		{
			return indicator.TradeFlowTutorial(Input);
		}

		public Indicators.TradeFlowTutorial TradeFlowTutorial(ISeries<double> input )
		{
			return indicator.TradeFlowTutorial(input);
		}
	}
}

#endregion
Output looks like this in the NinjaScript output primary window...




hyperscalper

Attached Thumbnails
Click image for larger version

Name:	Tutorial-Output-Signed-Volumes.PNG
Views:	304
Size:	25.4 KB
ID:	317863  
Started this thread
 
 
justtrader's Avatar
 justtrader 
San Francisco, CA
 
Experience: Intermediate
Platform: Ninja Trader, TOS
Trading: es, rty, cl, gc, nq, ym
Posts: 182 since May 2011
Thanks Given: 104
Thanks Received: 173

Thanks. This is a good start with T&S (Time & Sales?).

TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15276.75
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15276.75
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15276.75
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15277
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15277
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15276.5
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15276.5
TradeTime 9/24/2021 11:31:31 Retail Buy 2 @15276.5
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15276.75
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15276.75
TradeTime 9/24/2021 11:31:31 Retail Sell -2 @15276.75
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15277
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15277
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15276.5
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15277
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15276.75
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15276.75
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15277
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15277
TradeTime 9/24/2021 11:31:31 Retail Buy 1 @15277
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15277
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15277
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15277
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15277
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15276.75
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15276.75
TradeTime 9/24/2021 11:31:31 Retail Sell -1 @15276.75

JT


hyperscalper View Post
OK, I CAN SENSE YOUR EXCITEMENT SO HERE'S SOME STARTER
CODE. I quickly hacked this together...

If there are Bugs, please be kind to my fragile Ego...

Should be able to paste it in the NinjaScript editor; which I find very
annoying, but it's the easiest way, so long as things don't get too
complicated...

 
Code
#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
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
{
	public class TradeFlowTutorial : Indicator
	{
		protected override void OnStateChange()
		{
			if (State == State.SetDefaults)
			{
				Description									= @"in Futures.IO an exercise in developing a simple Trade Flow analyzer.";
				Name										= "TradeFlowTutorial";
				Calculate									= Calculate.OnEachTick;
				IsOverlay									= false;
				DisplayInDataBox							= false;
				DrawOnPricePanel							= false;
				DrawHorizontalGridLines						= true;
				DrawVerticalGridLines						= true;
				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					= true;
			}
			else if (State == State.Configure)
			{
				init();
			}
			else if (State == State.Realtime)
			{
			}
			else if (State == State.Terminated)
			{
				deInit();
			}
		}
		
		private void init() {
		}
		
		private static bool USE_NINJA_PRINT = true;
		
        private void ninjaPrint(string msg) { // primary tab
            if (!USE_NINJA_PRINT) return;
            PrintTo = PrintTo.OutputTab1;
            Print(msg);
        }

        private void ninjaPrint2(string msg) { // secondary tab
            if (!USE_NINJA_PRINT) return;
            PrintTo = PrintTo.OutputTab2;
            Print(msg);
        }

        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
        }



		private void deInit() {
		}
		
		private static bool LOG_RAW_VOLUME = true;
		
		private void reportTrade(int tradeRawVol, double price) {
			if ( LOG_RAW_VOLUME ) {
				if (tradeRawVol>0) {
					ninjaPrint("Retail Buy "+tradeRawVol+" @"+price);
				}
				else {
					ninjaPrint("       Retail Sell "+tradeRawVol+" @"+price);
				}
			}
			double compressedVol = compressSizeMinOneSigned( tradeRawVol );
			reportToIntegrator( compressedVol );
		}
		
		private void reportToIntegrator(double compressedVolArg) {
		}
		
		private double lastBidPrice = 0;
		private double lastAskPrice = 0;

		protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
		{
			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
			}
			return;
		}

		protected override void OnBarUpdate()
		{
			//Add your custom indicator logic here.
		}
	}
}

#region NinjaScript generated code. Neither change nor remove.

namespace NinjaTrader.NinjaScript.Indicators
{
	public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
	{
		private TradeFlowTutorial[] cacheTradeFlowTutorial;
		public TradeFlowTutorial TradeFlowTutorial()
		{
			return TradeFlowTutorial(Input);
		}

		public TradeFlowTutorial TradeFlowTutorial(ISeries<double> input)
		{
			if (cacheTradeFlowTutorial != null)
				for (int idx = 0; idx < cacheTradeFlowTutorial.Length; idx++)
					if (cacheTradeFlowTutorial[idx] != null &&  cacheTradeFlowTutorial[idx].EqualsInput(input))
						return cacheTradeFlowTutorial[idx];
			return CacheIndicator<TradeFlowTutorial>(new TradeFlowTutorial(), input, ref cacheTradeFlowTutorial);
		}
	}
}

namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
	public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
	{
		public Indicators.TradeFlowTutorial TradeFlowTutorial()
		{
			return indicator.TradeFlowTutorial(Input);
		}

		public Indicators.TradeFlowTutorial TradeFlowTutorial(ISeries<double> input )
		{
			return indicator.TradeFlowTutorial(input);
		}
	}
}

namespace NinjaTrader.NinjaScript.Strategies
{
	public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
	{
		public Indicators.TradeFlowTutorial TradeFlowTutorial()
		{
			return indicator.TradeFlowTutorial(Input);
		}

		public Indicators.TradeFlowTutorial TradeFlowTutorial(ISeries<double> input )
		{
			return indicator.TradeFlowTutorial(input);
		}
	}
}

#endregion
Output looks like this in the NinjaScript output primary window...




hyperscalper


TWYS NWYT (Price Advertises Opportunity; Time Regulates it; Volume Measures its Success/Failure ---- Dalton)
Thanked by:
 
 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


PUSHING THINGS A BIT FURTHER WITH A "STUPID-INTEGRATOR"

Hard-coded to reject singles, but lacking our Time Expiry Window; which will
be addressed in a future Integrator...

Obviously this simple hacked StupidIntegrator lacks the strict Time Expiry
Window, tapering, etc... which will coax much more info out. Right now, it
holds a FIXED number of data items, as you can see... more will come !!!

 
Code
#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
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
{
	public class TradeFlowTutorial : Indicator
	{
		protected override void OnStateChange()
		{
			if (State == State.SetDefaults)
			{
				Description									= @"in Futures.IO an exercise in developing a simple Trade Flow analyzer.";
				Name										= "TradeFlowTutorial";
				Calculate									= Calculate.OnEachTick;
				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
			}
			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(Brushes.Purple), PlotStyle.Line, "inventory"); // index
				inventoryIndex = indexCounter++; // starts with zero; plot idx
				Plots[inventoryIndex].Width = 4;
				
				init();
			}
			else if (State == State.Realtime)
			{
				isReady = true;
			}
			else if (State == State.Terminated)
			{
				isReady = false;
				deInit();
			}
		}
		
		private int indexCounter = 0;
		
		private int zeroIndex = 0; // assigned in config
		private int inventoryIndex = 0;
		
		private bool isReady = false;
		
		private void init() {
			myIntegrator = new StupidIntegrator(100);
			// allocate objects
		}
		
		private static bool USE_NINJA_PRINT = true;
		
        private void ninjaPrint(string msg) { // primary tab
            if (!USE_NINJA_PRINT) return;
            PrintTo = PrintTo.OutputTab1;
            Print(msg);
        }

        private void ninjaPrint2(string msg) { // secondary tab
            if (!USE_NINJA_PRINT) return;
            PrintTo = PrintTo.OutputTab2;
            Print(msg);
        }
		
		private StupidIntegrator myIntegrator = null;
		
		/* As its name suggests, this is Stupid...
		so it won't achieve our goals at all... */
		protected /*inner*/ class StupidIntegrator {
			private double[] dataArray = null;
			int size = 0;
			int ptr = 0;
			public StupidIntegrator(int sizeArg) { // constructor
				size = sizeArg;
				dataArray = new double[size];
				for(int i=0; i<size; i++) {
					dataArray[i] = 0;
				}
			}
			public void addData(double dataArg) {
				if (ptr>=size) {
					ptr = 0;
				}
				dataArray[ptr++] = dataArg;
			}
			public double getSum() {
				double sum = 0;
				for(int i=0; i<size; i++) {
					sum += dataArray[i];
				}
				return sum;
			}
		}

        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
        }



		private void deInit() {
			myIntegrator = null;
			// stop and deallocate objects
		}
		
		private static bool LOG_RAW_VOLUME = true;
		
		private int lastRawTradeVol = 0;
		
		int minTradeSizeForBigLot = 2;
		
		private void reportTrade(int tradeRawVol, double price) {
			if (Math.Abs(tradeRawVol) < minTradeSizeForBigLot) 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 );
			reportToIntegrator( compressedVol );
		}
		
		private void reportToIntegrator(double compressedVolArg) {
			myIntegrator.addData(compressedVolArg);
		}
		
		private double lastBidPrice = 0;
		private double lastAskPrice = 0;

		protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
		{
			if ( !isReady ) return;
			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
			}
			return;
		}

		protected override void OnBarUpdate()
		{
			Values[zeroIndex][0] = 0;
			double inventory = myIntegrator.getSum();
			Values[inventoryIndex][0] = inventory;
		}
	}
}

#region NinjaScript generated code. Neither change nor remove.

namespace NinjaTrader.NinjaScript.Indicators
{
	public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
	{
		private TradeFlowTutorial[] cacheTradeFlowTutorial;
		public TradeFlowTutorial TradeFlowTutorial()
		{
			return TradeFlowTutorial(Input);
		}

		public TradeFlowTutorial TradeFlowTutorial(ISeries<double> input)
		{
			if (cacheTradeFlowTutorial != null)
				for (int idx = 0; idx < cacheTradeFlowTutorial.Length; idx++)
					if (cacheTradeFlowTutorial[idx] != null &&  cacheTradeFlowTutorial[idx].EqualsInput(input))
						return cacheTradeFlowTutorial[idx];
			return CacheIndicator<TradeFlowTutorial>(new TradeFlowTutorial(), input, ref cacheTradeFlowTutorial);
		}
	}
}

namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
	public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
	{
		public Indicators.TradeFlowTutorial TradeFlowTutorial()
		{
			return indicator.TradeFlowTutorial(Input);
		}

		public Indicators.TradeFlowTutorial TradeFlowTutorial(ISeries<double> input )
		{
			return indicator.TradeFlowTutorial(input);
		}
	}
}

namespace NinjaTrader.NinjaScript.Strategies
{
	public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
	{
		public Indicators.TradeFlowTutorial TradeFlowTutorial()
		{
			return indicator.TradeFlowTutorial(Input);
		}

		public Indicators.TradeFlowTutorial TradeFlowTutorial(ISeries<double> input )
		{
			return indicator.TradeFlowTutorial(input);
		}
	}
}

#endregion
hyperscalper

Attached Thumbnails
Click image for larger version

Name:	Tutorial-with-Stupid-Integrator.PNG
Views:	389
Size:	52.4 KB
ID:	317870   Click image for larger version

Name:	Tutorial-with-Stupid-Integrator2.PNG
Views:	396
Size:	38.3 KB
ID:	317873   Click image for larger version

Name:	Tutorial-with-Stupid-Integrator3.PNG
Views:	311
Size:	46.6 KB
ID:	317874   Click image for larger version

Name:	Tutorial-with-Stupid-Integrator4.PNG
Views:	285
Size:	52.3 KB
ID:	317875   Click image for larger version

Name:	Tutorial-with-Stupid-Integrator5.PNG
Views:	304
Size:	48.1 KB
ID:	317880  
Started this thread

 



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