<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.2.2">Jekyll</generator><link href="https://jenil.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://jenil.net/" rel="alternate" type="text/html" /><updated>2025-05-10T15:51:06+00:00</updated><id>https://jenil.net/feed.xml</id><title type="html">Home</title><subtitle>Exploring and Sharing my Journey Through Hacks, Discoveries, and Learnings.</subtitle><author><name>Jenil Shah</name></author><entry><title type="html">Comparing Real-Time US Stock Options Data Providers - Which API is Right for You?</title><link href="https://jenil.net/p/compare-options-api/" rel="alternate" type="text/html" title="Comparing Real-Time US Stock Options Data Providers - Which API is Right for You?" /><published>2024-07-02T00:00:00+00:00</published><updated>2024-07-02T00:00:00+00:00</updated><id>https://jenil.net/p/compare-options-api</id><content type="html" xml:base="https://jenil.net/p/compare-options-api/"><![CDATA[<p>I am a hobbyist. I like to tinker with finacial instruments. And options are facinating instrument vehicles. Options provides leverage for your investments.</p>

<p>Exploratory space for options is vast. Taking variables of call, put, strike prices and expiry dates, there are more than 1k+ option contracts for each underlying asset. This results in explosion of exploratory space if you consider all the option contracts available in market. At any given point of time, there are more than 100k+ live option contracts.</p>

<p>What better way to explore and scan this space than using custom code? There are multiple ways to get insights or devise your algorithmic startegy or backtest your strategy for option contracts. You can use snasphots for backtesting or getting insights. However, in this post, we will focus on real time or near real time option data. We start with analysis of data provider for real time options for hobbyists. This blog isnt focused on data provider APIs for instituions or enterprises, but focuses more on hobbyists and hackers who are on limited budget.</p>

<p>Before we start to analyze different data providers, let me setup provide context on requirements through which lens I will analyze these data providers. These requirements are based on one of the option strategy I experimented with.</p>

<p><strong>Requirements lens</strong></p>

<p><em>Freshness:</em> 
Realtime or Near-realtime options data. I consider 15 mins delay as near-real time data.</p>

<p><em>Blanket API for Option chain</em>
I should be able to get all option contracts of underlying ticker with a single API call. The API shouldnt restrict me to only get option contracts of a ticker for single expiry.</p>

<p><em>[Nice to have] Websocket feed for Option contracts</em>
For streaming information, getting live feed is preferred. Its fragile architecture to call option chain API every 5 mins and do processing. In longer run, I would like to use streaming feed for option chain.</p>

<p><em>Price</em>
This will be deal breaker. I am hobbyist. I will be more conservative and say maximum I am willing to pay for Options API is $50 per month. Tbh, once you put your strategy in production, $50 is dirt cheap.</p>

<p><em>Backtesting</em>
How would you gain confidence in startegy, if you cannot backtest? There must be support of historical option chain API to help backtest</p>

<p><em>Option Entities as part of API call</em>
    1. Option ticker    <br />
    2. Expiry
    3. Call or Put<br />
    4. OHLC
    5. Last transaction price 
    6. Strike price
    7. Greeks
    8. IV
    9. Underlying instrument price
    10. Open Interest and Volume</p>

<p><em>Ease of implementation</em>
I would classify this as Low/Medium/High</p>

<p>Getting all these requirements for $50 is hard find. We will explore some data providers, and try to maintain balance between API features and requirements.</p>

<h3 id="data-providers-comparision">Data providers comparision:</h3>

<ol>
  <li><a href="https://www.thetadata.net/"><strong>Thetadata</strong></a></li>
</ol>

<table>
  <thead>
    <tr>
      <th>Requirement</th>
      <th>Data Provider</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Freshness</td>
      <td>Realtime</td>
    </tr>
    <tr>
      <td>Blanket API for Option chain</td>
      <td>Separate API call for Options chain and Greeks</td>
    </tr>
    <tr>
      <td>Websocket feed for Option contracts</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Price</td>
      <td>$80 for standard subscription</td>
    </tr>
    <tr>
      <td>Historical data</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Option Entities as part of API call</td>
      <td>Everything except second order Greeks</td>
    </tr>
    <tr>
      <td>Ease of implementation</td>
      <td>Hard</td>
    </tr>
  </tbody>
</table>

<h4 id="notes">Notes</h4>
<ul>
  <li>Thetadata is more suited for algo traders focusing on volatility and requiring millisecond updates.</li>
  <li>Thetadata requires to setup <a href="https://http-docs.thetadata.us/docs/theta-data-rest-api-v2/4g9ms9h4009k0-getting-started">Theta Termina</a> which is constantly running. I havent looked into how it works with details but that could add more complexity</li>
</ul>

<ol>
  <li><a href="https://www.marketdata.app/"><strong>MarketData</strong></a></li>
</ol>

<table>
  <thead>
    <tr>
      <th>Requirement</th>
      <th>Data Provider</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Freshness</td>
      <td>Realtime</td>
    </tr>
    <tr>
      <td>Blanket API for Option chain</td>
      <td><a href="https://www.marketdata.app/docs/api/options/chain">Single call</a></td>
    </tr>
    <tr>
      <td>Websocket feed for Option contracts</td>
      <td>No</td>
    </tr>
    <tr>
      <td>Price</td>
      <td>$30/month for annual subscription<br />$100/month for monthly subscriotion</td>
    </tr>
    <tr>
      <td>Historical</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Option Entities as part of API call</td>
      <td>Everything</td>
    </tr>
    <tr>
      <td>Ease of implementation</td>
      <td>Easy</td>
    </tr>
  </tbody>
</table>

<p>Some of the Marketdata features:</p>

<table>
  <thead>
    <tr>
      <th>Feature</th>
      <th>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Paid vs Trial plans</td>
      <td>Marketdata provides ability to have try out plans without paying for 30 days. <br />Some features differ between Paid and Trial plans</td>
    </tr>
    <tr>
      <td>Standard vs Premium APIs</td>
      <td>Premium APIs and featrues can only be used in paid plans. <br />Standard APIs can be used in Trail plans</td>
    </tr>
    <tr>
      <td>Premium APIs</td>
      <td>Earnings api<br />Live vs Cached feed feature</td>
    </tr>
    <tr>
      <td>Cached feed feature</td>
      <td>Cached feed allows to get data from cache. Not guaranteed that its going to be realtime. <br />Delay could be between seconds to days. <br />Advantage is it will only charge 1 credit for responses that return multiple quotes, like option chains. That would be huge cost savings</td>
    </tr>
    <tr>
      <td>SDKs</td>
      <td>Developed Go SDK. Python SDK in development</td>
    </tr>
    <tr>
      <td>Option chain filters</td>
      <td><a href="https://www.marketdata.app/docs/api/options/chain#request-parameters">Multiple filters</a></td>
    </tr>
  </tbody>
</table>

<h4 id="notes-1">Notes</h4>

<ul>
  <li>Their credit based pricing model isnt much favorable for option chains. Everytime one ticker(option contract) is included in the response, its considered one credit. Lets say, we get an option chain with 40 option contracts data. This will be considered as usage of 40 credits. Credits can be exhausted really quick in this manner.
    <ul>
      <li>There is an option to retrieve cached feed vs live feed. If you use cached feed, you are only charged 1 credit per response irrespective of number of quotes returned. Using cached feed is a premium feature.</li>
    </ul>
  </li>
</ul>

<h4 id="playing-around-with-option-chain-api">Playing around with Option Chain API</h4>

<ul>
  <li>Option Chain API only support a single expiration date per call. It cannot support multiple expiration date. ie. The filters need to resolve to single expiration date</li>
  <li>I am running the code on 07/04/2024. If no paramater is specified, it returns option chain on 07/19. Not sure, why?</li>
  <li>If I set “weekly” to “true”, it returns option chain of immediate week. This makes sense. If I set “weekly” to “false”, it returns option chain on 07/19. Why is it not returning 07/12 option chain?</li>
  <li>Setting “monthly” to “true”, doesnt return monthly option chain. It still returns 07/19 option chain for some reason.</li>
  <li>For my usecase, I should call the expiration endpoint first. Take top x dates and call option chain endpoint simultaneously.</li>
</ul>]]></content><author><name>Jenil Shah</name></author><summary type="html"><![CDATA[I am a hobbyist. I like to tinker with finacial instruments. And options are facinating instrument vehicles. Options provides leverage for your investments.]]></summary></entry><entry><title type="html">Microsoft Todo Graph API bug and its cascading effects</title><link href="https://jenil.net/p/ms-todo-api-bug/" rel="alternate" type="text/html" title="Microsoft Todo Graph API bug and its cascading effects" /><published>2024-02-24T00:00:00+00:00</published><updated>2024-02-24T00:00:00+00:00</updated><id>https://jenil.net/p/ms-todo-api-bug</id><content type="html" xml:base="https://jenil.net/p/ms-todo-api-bug/"><![CDATA[<p>For those familiar with <a href="https://en.wikipedia.org/wiki/Wunderlist">Wunderlist</a>, it played an essential role in my approach to managing tasks. Following its acquisition by Microsoft, I smoothly shifted to using Microsoft Todo. This transition was straightforward. Although Microsoft removed some functionalities after rebranding Wunderlist as MS Todo, they introduced the “My Day” feature, which enhanced the user experience with its intuitive design.</p>

<p>When you put MS Todo side by side with other task management applications like Todoist or TickTick, it’s clear that MS Todo falls short in terms of features. Despite this, I’ve continued to use MS Todo for its simplicity. Over time, I’ve entered over 6000 tasks into the Microsoft Todo app. Moreover, the effort and time required to adapt to a new task management app are substantial, and in my view, the cost of switching and habit formation doesn’t justify the benefits. However, it appears that Microsoft’s focus isn’t so much on developing MS Todo as a standalone application but rather on enhancing its integration within the broader Microsoft 365 ecosystem.</p>

<p>In my automation workflows, I frequently utilize <a href="https://ifttt.com/">IFTTT</a> and <a href="https://zapier.com/">Zapier</a> to capture tasks into the MS Todo Inbox. For several months, I’ve been dealing with a persistent bug in MS Todo that seems to have gone unnoticed by many MS Todo users and their integration partners. This issue came to my attention while setting up an IFTTT Applet and using “Create task in task list” MS Todo action. Despite having more than 20 task lists, it only displays the “Flagged email” task list for me. At first, I suspected the problem was with IFTTT, so I contacted their support team. However, after a month of back-and-forth communication, we still didnt reach conclusion.</p>

<p><img src="/images/image-assets/flagged-emails.jpg" alt="" /></p>

<p>I noticed similar issues with Zapier and when using the <a href="https://github.com/johandebeurs/alfred-mstodo-workflow">MS Todo Alfred workflow</a>. This made it crystal clear that the problem lies with the Microsoft Graph APIs themselves, rather than with their client-side implementations.</p>

<p>My curiosity piqued and I started going through their <a href="https://learn.microsoft.com/en-us/graph/api/resources/todo-overview?view=graph-rest-1.0">API documentation</a>. It appears that Microsoft offers two similar APIs for retrieving all Task lists: <a href="https://learn.microsoft.com/en-us/graph/api/todo-list-lists?view=graph-rest-1.0&amp;tabs=http">List task lists</a> and <a href="https://learn.microsoft.com/en-us/graph/api/todotasklist-delta?view=graph-rest-1.0&amp;tabs=http">Get task list delta</a>. The former appears to be more definitive, while the latter is preferred for clients that wish to stay synchronized with the server to enhance performance. To examine the API responses, I utilized the <a href="https://developer.microsoft.com/en-us/graph/graph-explorer">Microsoft Graph Explorer</a> to make calls to both APIs. Below are my findings:</p>

<p><strong>List task lists API</strong></p>

<p><em>Request</em></p>

<p><code class="language-plaintext highlighter-rouge">https://graph.microsoft.com/v1.0/me/todo/lists/</code></p>

<p><em>Response</em></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="s">"value"</span><span class="p">:</span> <span class="p">[</span>

		<span class="p">{</span>
		<span class="s">"@odata.etag"</span><span class="p">:</span> <span class="s">"dummyTag"</span><span class="p">,</span>
		<span class="s">"displayName"</span><span class="p">:</span> <span class="s">"Flagged Emails"</span><span class="p">,</span>
		<span class="s">"isOwner"</span><span class="p">:</span> <span class="n">true</span><span class="p">,</span>
		<span class="s">"isShared"</span><span class="p">:</span> <span class="n">false</span><span class="p">,</span>
		<span class="s">"wellknownListName"</span><span class="p">:</span> <span class="s">"flaggedEmails"</span><span class="p">,</span>
		<span class="s">"id"</span><span class="p">:</span> <span class="s">"dummyID"</span>
		<span class="p">}</span>
	<span class="p">]</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Even though I have 20+ lists in Microsoft Todo, it only gets me “Flagged Emails list”. This is a <strong>bug</strong></p>

<p><strong>Get Task Lists API</strong></p>

<p><em>Request</em>
<code class="language-plaintext highlighter-rouge">https://graph.microsoft.com/v1.0/me/todo/lists/microsoft.graph.delta()</code></p>

<p><em>Response</em></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="p">{</span>
	<span class="s">"displayName"</span><span class="p">:</span> <span class="s">"Flagged Emails"</span><span class="p">,</span>
	<span class="s">"isOwner"</span><span class="p">:</span> <span class="n">true</span><span class="p">,</span>
	<span class="s">"isShared"</span><span class="p">:</span> <span class="n">false</span><span class="p">,</span>
	<span class="s">"wellknownListName"</span><span class="p">:</span> <span class="s">"flaggedEmails"</span><span class="p">,</span>
	<span class="s">"id"</span><span class="p">:</span> <span class="s">"dummy"</span>
<span class="p">},</span>

<span class="p">{</span>
	<span class="s">"displayName"</span><span class="p">:</span> <span class="s">"Tasks"</span><span class="p">,</span>
	<span class="s">"isOwner"</span><span class="p">:</span> <span class="n">true</span><span class="p">,</span>
	<span class="s">"isShared"</span><span class="p">:</span> <span class="n">false</span><span class="p">,</span>
	<span class="s">"wellknownListName"</span><span class="p">:</span> <span class="s">"defaultList"</span><span class="p">,</span>
	<span class="s">"id"</span><span class="p">:</span> <span class="s">"dummy"</span>
<span class="p">},</span>

<span class="p">{</span>
	<span class="s">"displayName"</span><span class="p">:</span> <span class="s">"List A"</span><span class="p">,</span>
	<span class="s">"isOwner"</span><span class="p">:</span> <span class="n">true</span><span class="p">,</span>
	<span class="s">"isShared"</span><span class="p">:</span> <span class="n">false</span><span class="p">,</span>
	<span class="s">"wellknownListName"</span><span class="p">:</span> <span class="s">"none"</span><span class="p">,</span>
	<span class="s">"id"</span><span class="p">:</span> <span class="s">"dummy"</span>
<span class="p">},</span>

<span class="p">{</span>
	<span class="s">"displayName"</span><span class="p">:</span> <span class="s">"List B"</span><span class="p">,</span>
	<span class="s">"isOwner"</span><span class="p">:</span> <span class="n">true</span><span class="p">,</span>
	<span class="s">"isShared"</span><span class="p">:</span> <span class="n">false</span><span class="p">,</span>
	<span class="s">"wellknownListName"</span><span class="p">:</span> <span class="s">"none"</span><span class="p">,</span>
	<span class="s">"id"</span><span class="p">:</span> <span class="s">"dummy"</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This correctly displays all my lists in MS Todo. I’m uncertain whether the Delta API was recently introduced or has been available since the beginning. However, it’s clear that the initial requests from the <code class="language-plaintext highlighter-rouge">list taskLists</code> API and the <code class="language-plaintext highlighter-rouge">list taskLists delta</code> API follow different patterns.</p>

<p>I found only a single mention of this problem online. Microsoft has yet to notice the issue, which negatively impacts the user experience for some customers. One reason this problem might have remained under the radar is that it primarily affects power users who have multiple task lists, numerous tasks, or have been using MS Todo for an extended period. Regular customers with minimal use of MS Todo are not affected by this issue.</p>

<p>Microsoft doesnt expose easy way to submit bug reports for Microsoft Graph API. However, I have submitted feedback <a href="https://feedbackportal.microsoft.com/feedback/idea/0cf859ac-f8d3-ee11-92bd-000d3a7a5824">here</a> in Microsoft Graph portal.</p>

<h3 id="cascading-effect">Cascading effect</h3>

<p>Incorporating abstractions undoubtedly offers benefits in terms of speed and convenience, yet it also introduces costs related to maintenance and traceability. Within a web of API integrations, a single bug at the system’s core can ripple through to all its clients and, ultimately, impact the business. Furthermore, the cost of diagnosing errors becomes significant when integration partners are in the dark about the root cause of an issue. As a customer, my instinct would be to report a problem to IFTTT, never suspecting that the real issue lies within the Microsoft API itself. When a minor bug occurs in a tool designed for further abstraction, it’s like tossing a pebble into a pond: the resulting ripples extend broadly, influencing not just an individual user but the entire network of those connected to the system.</p>

<p>This makes me wonder and appreciate how automation tools(eg. IFTTT, Zapier, Make, Tray.io) runs their operations while maintaining more than 5k+ API integrations accross more than 800+ different SaaS services.</p>]]></content><author><name>Jenil Shah</name></author><summary type="html"><![CDATA[For those familiar with Wunderlist, it played an essential role in my approach to managing tasks. Following its acquisition by Microsoft, I smoothly shifted to using Microsoft Todo. This transition was straightforward. Although Microsoft removed some functionalities after rebranding Wunderlist as MS Todo, they introduced the “My Day” feature, which enhanced the user experience with its intuitive design.]]></summary></entry><entry><title type="html">Guide to Markdown syntax supported by Jekyll</title><link href="https://jenil.net/p/guide-to-markdown-syntax-supported-by-jekyll/" rel="alternate" type="text/html" title="Guide to Markdown syntax supported by Jekyll" /><published>2024-01-18T00:00:00+00:00</published><updated>2024-01-18T00:00:00+00:00</updated><id>https://jenil.net/p/guide-to-markdown-syntax-supported-by-jekyll</id><content type="html" xml:base="https://jenil.net/p/guide-to-markdown-syntax-supported-by-jekyll/"><![CDATA[<p>Hey everyone! I just got my blog up and running with the <a href="https://mademistakes.com/work/jekyll-themes/minimal-mistakes/">Minimal Mistakes</a> Jekyll theme. It was a bit of a toss-up between Hugo and Jekyll, but I ended up choosing Jekyll because it seemed easier to get along with. Plus, I thought if I ever needed to tweak something, it’d be simpler to do in Ruby than in Go for me.</p>

<p>Right now, I’m diving into learning all the markdown syntaxes that Jekyll and Minimal Mistakes support. I’m pretty sure it handles all the Github Markdown, but I’ll be listing and exploring them here on this blog.</p>

<p><strong>How am I going to get all the markdown syntaxes?</strong></p>

<p><a href="https://mademistakes.com/work/jekyll-themes/minimal-mistakes/">Minimal Mistakes</a> was created by Micheal Rose. On Minimal Mistakes website, he provides examples of different Posts. I am going to scan through those and understand multiple markdown syntaxes supported by Minimal mistakes.</p>

<p><a href="https://mmistakes.github.io/minimal-mistakes/year-archive/">Demo of Posts</a></p>

<p><a href="https://github.com/mmistakes/minimal-mistakes/tree/master/docs/_posts">Source code of Posts</a></p>

<p><strong>Syntax Highlighting</strong></p>

<p><a href="https://mmistakes.github.io/minimal-mistakes/markup-syntax-highlighting/">Demo page</a></p>

<p><a href="https://raw.githubusercontent.com/mmistakes/minimal-mistakes/master/docs/_posts/2013-08-16-markup-syntax-highlighting.md">Source code</a></p>

<h3 id="about-me">About me</h3>

<p>Hi, I’m Mona. You might recognize me as GitHub’s mascot.</p>

<table>
  <thead>
    <tr>
      <th>Rank</th>
      <th>Languages</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>JavaScript</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Python</td>
    </tr>
    <tr>
      <td>3</td>
      <td>SQL</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Jenil Shah</name></author><category term="markdown" /><category term="blog" /><summary type="html"><![CDATA[Hey everyone! I just got my blog up and running with the Minimal Mistakes Jekyll theme. It was a bit of a toss-up between Hugo and Jekyll, but I ended up choosing Jekyll because it seemed easier to get along with. Plus, I thought if I ever needed to tweak something, it’d be simpler to do in Ruby than in Go for me.]]></summary></entry></feed>