-- Id: 3156
-- More information about this indicator can be found at:
-- http://fxcodebase.com/code/viewtopic.php?f=17&t=2640

--+------------------------------------------------------------------+
--|                               Copyright © 2019, Gehtsoft USA LLC |
--|                                            http://fxcodebase.com |
--+------------------------------------------------------------------+
--|                                      Developed by : Mario Jemic  |
--|                                          mario.jemic@gmail.com   |
--+------------------------------------------------------------------+
--|                                 Support our efforts by donating  |
--|                                    Paypal: https://goo.gl/9Rj74e |
--+------------------------------------------------------------------+
--|                                Patreon :  https://goo.gl/GdXWeN  |
--|                    BitCoin : 15VCJTLaz12Amr7adHSBtL9v8XomURo9RF  |
--|                BitCoin Cash: 1BEtS465S3Su438Kc58h2sqvVvHK9Mijtg  |
--|           Ethereum : 0x8C110cD61538fb6d7A2B47858F0c0AaBd663068D  |
--|                   LiteCoin : LLU8PSY2vsq7B9kRELLZQcKf5nJQrdeqwD  |
--+------------------------------------------------------------------+

function Init()
	indicator:name("Market Profile")
	indicator:description("Market Profile")
	indicator:requiredSource(core.Bar)
	indicator:type(core.Indicator)
	indicator.parameters:addGroup("Calculation")
	indicator.parameters:addInteger("BoxSize", "Size of price box in pips", "", 5)
	indicator.parameters:addInteger("Val", "Value Area Percentage", "", 70)
	indicator.parameters:addBoolean("Consolidated", "Consolidated", "", true)
	indicator.parameters:addBoolean("Hide", "Hide TPO", "", false)
	indicator.parameters:addString("Type", "Alphabet", "", "Alphabet")
	indicator.parameters:addStringAlternative("Type", "Alphabet", "", "Alphabet")
	indicator.parameters:addBoolean("LAST", "Show Only Last Period", "", true)
	indicator.parameters:addBoolean("ShowValueArea", "Show Value Area", "", true)
	 

	indicator.parameters:addGroup("Style")
	indicator.parameters:addStringAlternative("Type", "Dots", "", "Dot")
	indicator.parameters:addColor("POC", "POC Color", "", core.rgb(127, 127, 127))
	indicator.parameters:addInteger("Size", "Font Size", "", 8)
	indicator.parameters:addColor("VA_color", "VA Color", "", core.rgb(127, 127, 127))
	indicator.parameters:addInteger("VA_transp", "VA transparency, %", "", 50, 0, 100);
end

local Size
local BoxSize
local source
local first
local Consolidated
local Hide
local Value
local s
local e
local ValueArea
local Type
local BOX
local ShowValueArea;
local LAST

local ABC = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
	"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
	"1","2","3","4","5","6","7","8","9","10"}
local PocMark
local BarSize
local vah;
local val;
-- Routine
function Prepare(nameOnly)
	Value = instance.parameters.Val
	BoxSize = instance.parameters.BoxSize
	Hide = instance.parameters.Hide
	Consolidated = instance.parameters.Consolidated
	Type = instance.parameters.Type
	Size = instance.parameters.Size
	LAST = instance.parameters.LAST
	ShowValueArea= instance.parameters.ShowValueArea;

	source = instance.source
	first = source:first()

	BOX = BoxSize * source:pipSize()

	local name = profile:id() .. "(" .. source:name() .. ", " .. BoxSize .. ")"
	instance:name(name)

	if (nameOnly) then
		return
	end

	
	
	if not ShowValueArea then	 
	vah = instance:addStream("VAH", core.LINE_NONE, "VAH", "VAH", instance.parameters.VA_color, 0)
	val = instance:addStream("VAL", core.LINE_NONE, "VAL", "VAL", instance.parameters.VA_color, 0)
    else
	vah = instance:addStream("VAH", core.Line, "VAH", "VAH", instance.parameters.VA_color, 0)
	val = instance:addStream("VAL", core.Line, "VAL", "VAL", instance.parameters.VA_color, 0)
	instance:createChannelGroup("VA", "VA", vah, val, instance.parameters.VA_color, instance.parameters.VA_transp);
	end

	PocMark = instance:createTextOutput("POC", "POC", "Wingdings", Size, core.H_Center, core.V_Center, instance.parameters.POC, 0)
	local s1, e2
	s1, e2 = core.getcandle(source:barSize(), core.now(), 0, 0)
	BarSize = e2 - s1
end

local date
local Filter
local FLAG

function Update(period)
	date = source:date(period)
	local Table = core.dateToTable(date)
	local Last = source:date(source:size() - 1)
	local LastTable = core.dateToTable(Last)

	if LAST then
		if Table.month == LastTable.month and Table.day == LastTable.day then
			MP(period, Table.month * 31 + Table.day)
		end
		if FLAG ~= Table.day then
			FLAG = Table.day
			core.host:execute("removeAll")
		end
	else
		MP(period, Table.month * 31 + Table.day)
	end
end

function CalcVAMinMax(profile, poc, area)
	local max = poc;
	local min = poc;
	local summ = profile[poc];
	while (summ < area) do
		local up = profile[max + 1] or 0;
		local down = profile[min - 1] or 0;
		if up > down then
			summ = summ + up;
			max = max + 1;
		else
			summ = summ + down;
			min = min - 1;
		end
	end
	return min, max;
end

function MP(period, n)
	local s, e = core.getcandle("D1", date, 0, 0)
	local start = core.findDate(source, s, false)
	local finish = core.findDate(source, e, false)
	if period < finish or start <= first or finish > source:size() - 1 then
		return
	end
	local min, max = mathex.minmax(source, start, finish)
	local POC
	local Profile = {}
	local Label = {}
	local Total = 0
	for j = start, finish, 1 do
		local Count = 0
		for price = min, max, BOX do
			Count = Count + 1
			if Profile[Count] == nil then
				Profile[Count] = 0
			end

			if Label[Count] == nil then
				Label[Count] = " "
			end

			if not Consolidated then
				if price > source.high[j] or price + BOX < source.low[j] then
					Label[Count] = Label[Count] .. "  "
				end
			end

			if price >= source.low[j] and price + BOX <= source.high[j] 
				or price <= source.low[j] and price + BOX >= source.low[j] 
				or price <= source.high[j] and price + BOX >= source.high[j]
			then
				Profile[Count] = Profile[Count] + 1
				if 1 + finish - start > 60 or Type == "Dot" then
					Label[Count] = Label[Count] .. "."
				else
					Label[Count] = Label[Count] .. ABC[1 + j - start]
				end
				Total = Total + 1
			end

			if price == min then
				POC = Count
			elseif Profile[POC] < Profile[Count] then
				core.host:execute("drawLabel", n * 1000 + 200 + POC, source:date(start) - BarSize, min + POC * BOX, tostring(Profile[POC]))
				POC = Count
			end

			ValueArea = Value * (Total / 100)
			if Count == POC then
				core.host:execute("drawLabel", n * 1000 + 200 + Count, source:date(start) - BarSize, min + Count * BOX, "(" .. Profile[POC] .. ")")
			else
				core.host:execute("drawLabel", n * 1000 + 200 + Count, source:date(start) - BarSize, min + Count * BOX, tostring(Profile[Count]))
			end

			if not Hide then
				core.host:execute("drawLabel", n * 1000 + 100 + Count, source:date(start), min + Count * BOX, Label[Count])
			end
		end

		PocMark:set(j, min + POC * BOX, "\108")
		local va_min, va_max = CalcVAMinMax(Profile, POC, ValueArea);
		vah[j] = min + va_max * BOX;
		val[j] = min + va_min * BOX;
	end

	if 1 + period - start > 60 or Type == "Dot" then
		core.host:execute("drawLabel", n * 1000 + 1, source:date(start), max + 2 * BOX, " TSO Count (" .. Total .. "), Value Area(" .. ValueArea .. "), Last TSO " .. "(.)" .. ", POC (" .. min + POC * BOX .. ")")
	else
		core.host:execute("drawLabel", n * 1000 + 1, source:date(start), max + 2 * BOX, " TSO Count (" .. Total .. "), Value Area(" .. ValueArea .. "), Last TSO (" .. ABC[1 + period - start] .. "), POC (" .. min + POC * BOX .. ")")
	end
end
