Slightly change in imports, like importing totally unrelated Nim module, may break your program. And it happens silently, so you won't notice it.
As far as I understand why it works that way, is related to how Nim symbol overload works, I already discussed it in another thread. But that's a theory. The practical consequences of this issue is that your program is very unstable and slight change can break it.
Finally, isolated the problem, playground
import std/[json, jsonutils, strformat]
type SEC* = tuple
symbol: string
exchange: string
currency: string
proc somefn*(sec:SEC) =
discard sec.to_json
type IBSymbol* = tuple[symbol: string, exchange: string, currency: string]
func `$`*(s: IBSymbol): string = fmt"{s.symbol} {s.exchange} {s.currency}"
func to_json_hook*(v: IBSymbol): JsonNode = ($v).to_json
echo ("a", "b", "c").IBSymbol.to_json.`$`
Output
{"symbol":"a","exchange":"b","currency":"c"}
Seems like the problem is because there's the SEC tuple with exactly the same structure as IBSymbol.