Commitment of Traders: Total
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TradingView

//@version=5
indicator("Commitment of Traders: Total", "COT Total", format = format.volume)

import TradingView/LibraryCOT/2 as cot

// Tooltips.
var string TT_MODE = 
  "Determines which information from the chart's symbol will be used to fetch COT data:\n
  • 'Root' uses the root of a futures symbol ('ES' for 'ESH2020').\n
  • 'Base currency' uses the base currency in a forex pair ('EUR' for 'EURUSD').\n
  • 'Currency' uses the quote currency, i.e., the currency the symbol is traded in ('JPY' for 'TSE:9984' or 'USDJPY').\n
  • 'Auto' tries all modes, in turn.\n
  If no COT data can be found, a runtime error is generated."
var string TT_OVERRIDE = 
  "Instead of letting the script generate the CFTC COT code from the chart and the above selections when this field is empty, you can specify an unrelated CFTC COT code here, e.g., 001602 for wheat futures."

// Inputs.
string selectionModeInput  = input.string("Auto",    "COT selection mode", options = ["Auto", "Root", "Base currency", "Currency"], tooltip = TT_MODE)
string includeOptionsInput = input.string("Futures", "Futures/Options",    options = ["Futures", "Options", "Futures + Options"])
string selectedTypeInput   = input.string("Long",    "Display",            options = ["Long", "Short", "Long + Short", "Long - Short", "Long %"])
string userCFTCCodeInput   = input.string("",        "CFTC Code",          tooltip = TT_OVERRIDE)

// Chart-based or user-based CFTC code.
var string cftcCode = userCFTCCodeInput != "" ? userCFTCCodeInput : cot.convertRootToCOTCode(selectionModeInput)

dataRequest(cftcCode, metricName, isLong) =>
    futuresOnlyTicker = cot.COTTickerid("Legacy", cftcCode, false, metricName, isLong ? "Long" : "Short", "All")
    futuresWithOptionsTicker = cot.COTTickerid("Legacy", cftcCode, true, metricName, isLong ? "Long" : "Short", "All")
    futuresOnly = request.security(futuresOnlyTicker, "1D", close, ignore_invalid_symbol = true)
    futuresWithOptions = request.security(futuresWithOptionsTicker, "1D", close, ignore_invalid_symbol = true)
    
    if barstate.islastconfirmedhistory and (na(futuresOnly) or na(futuresWithOptions))
        runtime.error(str.format("Could not find relevant COT data based on the current symbol and the COT selection  mode `{0}`.", selectionModeInput))
    
    switch includeOptionsInput
    	"Futures" => futuresOnly
    	"Options" => futuresWithOptions - futuresOnly
    	"Futures + Options" => futuresWithOptions
		=> na

metric(cftcCode, metricCode) =>
	cotLong = dataRequest(cftcCode, metricCode, true)
	cotShort = dataRequest(cftcCode, metricCode, false)
	switch selectedTypeInput
		"Long" => cotLong
		"Short" => cotShort
		"Long + Short" => cotLong + cotShort
		"Long - Short" => cotLong - cotShort
		"Long %" => cotLong / (cotLong + cotShort) * 100
		=> na

largeTraders = metric(cftcCode, "Noncommercial Positions")
smallTraders = metric(cftcCode, "Nonreportable Positions")
commercialHedgers = metric(cftcCode, "Commercial Positions")

// Plot COT data.
plot(largeTraders, color=color.red, title="Large Traders", style=plot.style_stepline_diamond)
plot(smallTraders, color=color.blue, title="Small Traders", style=plot.style_stepline_diamond)
plot(commercialHedgers, color=color.green, title="Commercial Hedgers", style=plot.style_stepline_diamond)

// Display the COT ticker in the lower left.
var table displayedSymbol = table.new(position.bottom_left, 1, 1)
if barstate.isfirst
    color TEXT_COLOR = color.white
    color BG_COLOR   = color.new(color.blue, 50)
    string dataCOT = userCFTCCodeInput != "" ? userCFTCCodeInput : cot.convertRootToCOTCode(selectionModeInput, false)
    string txt = str.format("COT data for {0} {1}\nCFTC Code: {2}", dataCOT, includeOptionsInput, cftcCode)
    table.cell(displayedSymbol, 0, 0, txt, text_halign = text.align_left, text_color = TEXT_COLOR, bgcolor = BG_COLOR)