Published
Edited
Dec 10, 2019
2 forks
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
// mutable w = {
// let _width = 500;
// yield _width;

// while (true) {
// await Promises.delay(500);
// if (_width !== window.innerWidth) {
// _width = window.innerWidth;
// yield _width;
// }
// }
// }
// mutable w = {
// d3.select('#SVG')
// }
mutable w = 900;
// {
// while (true) {
// await Promises.delay(2000);
// yield window.innerWidth;
// }
// }
Insert cell
wrapper = html`<div id="${DOM.uid().id}">`
Insert cell
height = 500
Insert cell
// svg = d3
// .select(wrapper)
// .node()
// .appendChild(View)
Insert cell
Insert cell
md`## Intro`
Insert cell
md`

In Part 4 the chart will tick as option trades take place during the day. This is my favorite part. This gives us an easy look into how the market is situated. This was already in process but I felt stuck and struck with lack of direction and focus before even attempting it when [this came out](https://www.observablehq/@d3/bar-chart-race-explained)

The chart below is a static look at what the chart looked look at the end of the day. But if we bring in the \`SALES-LOG\` we can really piece it all together.

Up until Part 2 we were using live data but now we have to switch to mock data first

The reason is that there is no live \`sales-log\` available.
`
Insert cell
mock_data={
let options=AllOptions;
let salesLog = SalesLog;
return {options,salesLog}
}
Insert cell
MinutesForFrames = d3.groups(SalesLog, d =>
[...(DT + ' ' + d.DT)].fill(0, 17, 19).join('')
)
Insert cell
Calls = AllOptions.filter(x => x.OptionCode.optionType === 'C')
Insert cell
md`## UTILITY`
Insert cell
yAxis = (g, y_scale) =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(
d3
.axisLeft(y_scale)
.ticks(5)
.tickSize(w - (margin.left + margin.right))
.tickSizeOuter(0)

.tickPadding(3)
)
.call(g => g.select(".domain").remove())
.call(g =>
g
.select(".tick:last-of-type text")
.clone()
.attr("y", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.attr('transform', 'rotate(-90)')
.text(d => PROPERTY)
)
Insert cell
//NaN values are at the top of the chart
Insert cell
AllOptions
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
md`
## Configs

#### Market Parameters`
Insert cell
PROPERTY = 'Volume' //'volume'
Insert cell
md`### Visual Parameters`
Insert cell
margin = ({ top: 20, right: 50, bottom: 20, left: 30 })
Insert cell
padding = ({})
Insert cell
box = [4, 6]
Insert cell
background = '#0A0A0A'
Insert cell
Insert cell
Insert cell
Insert cell
md`## VIEWS`
Insert cell
md`### Option Chains Chart`
Insert cell
OptionChainChart = Chart_OptionSeries(AllOptions)
Insert cell
md`### Call Chart`
Insert cell
CallChart = Chart_OptionSeries(Calls)
Insert cell
md`### Put Chart`
Insert cell
PutChart = Chart_OptionSeries(
AllOptions.filter(x => x.OptionCode.optionType === 'P')
)
Insert cell
md`### Legend`
Insert cell
Legend = {
let seriesLabels = OptionSeries.map(x => {
let obj = d3.timeFormat('%y-%b-%d')(d3.timeParse('%s')(x.expirationDate));
return obj;
});

let seriesScale = d3
.scaleOrdinal()
.range(expirations.map(x => color(x)))
.domain(expirations);
let view = html`<svg style="width:100%; height:auto;">`;

let h = 50;

const svg = d3
.select(view)
.attr('viewBox', [0, 0, w, h])
.style('background', '#222');
yield svg.node();
let legend = svg.append("g").classed('custom-legend', true);
legend
.selectAll('.legend-key')
.data(expirations, d => d)
.join('circle')
.attr('class', d => d + ' legend-key')
.attr('cx', (d, i) => (w / expirations.length) * i + 25)
.attr('cy', 25)
.attr('fill', d => color(d))
.attr('r', 5);

legend
.selectAll('.legend-value')
.data(expirations, d => d)
.join('text')
.attr('class', d => 'legend-value ' + d)
.attr('x', (d, i) => (w / expirations.length) * i + 25)
.attr('y', 35)
.attr('stroke', 'white')
.attr('font-size', '15px')
.text((d, i) => d3.timeFormat('%d %b %Y')(d3.timeParse('%s')(d)))
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'middle');
}
Insert cell
Insert cell
strikeExtent = d3.extent(AllOptions.map(x => x.Strike))
Insert cell
selectedPropertyExtent = AllOptions.map(x => x[PROPERTY])
Insert cell
expirations = OptionSeries.map(x =>
d3.timeFormat('%s')(d3.timeParse('%d %b %y')(x[0]))
)
Insert cell
strikes = [
...new Set([...AllOptions.map(x => +x.strike)].sort((a, b) => a - b))
]
Insert cell
AllOptions = d3.merge(OptionChain.map(x => d3.merge([x.calls, x.puts])))
Insert cell
OptionChain = OptionSeries.map(a => {
let obj = {};
let alloptions = a[1].map(x => {
x.OptionCode = short_option_code(x["Option Code"]);
x.volume = x.Volume;
x.openInterest = x['Open.Int'];
x.strike = x.Strike;
x.expirationDate = new Date(x.OptionCode.label);
return x;
});

obj.calls = alloptions.filter(y => y.OptionCode.optionType === 'C');
obj.puts = alloptions.filter(y => y.OptionCode.optionType === 'P');
obj.expirationDate = d3.timeFormat('%s')(d3.timeParse('%d %b %y')(a[0]));
return obj;
})
Insert cell
// OptionChain = (await OptionChainFetcher(SYMBOL)).map(
// x => x.optionChain.result[0].options[0]
// )
Insert cell
Insert cell
SYMBOL='IBM'
Insert cell
DT = "2019-10-25"
Insert cell
OptionSeries = StockDay.options
Insert cell
SalesLog = d3.csvParse(
await FileAttachment('IBM-SalesLog-2019-10-25.csv').text(),
d3.autoType
)
Insert cell
// Option Code Volume Open.Int Delta Gamma Theta Vega Rho Impl Vol Theo Price ROC ROR Prob.ITM Prob.Touch Prob.OTM Cov Return Max Cov Return Mark Size BS AS %Change Last LAST LX Open High Low BID BX ASK AX Exp Strike % Change
Insert cell
StockDay = {
return {
...dataCredentials,
info: ListingInfo,
options: OptionChains
};
}
Insert cell
ListingInfo = {
let obj = {
...d3.csvParse(([textRows[3],textRows[4]]).join(`\n`),d3.autoType)[0],
...d3.csvParse(([textRows[7],textRows[8]]).join(`\n`),d3.autoType)[0]
}
obj.Volume=+parseNumbers(obj.Volume)
obj['Ex Div.Date']=exDivParser(obj['Ex Div.Date'])
obj.Shares=+parseNumbers(obj.Shares)
return obj;
}
Insert cell
md`## OPTIONS`
Insert cell
OptionChains = d3.groups(Options,k=>k.Exp)
Insert cell
Options.map(x => Object.keys(x).length)
Insert cell
Options = d3.merge(parseRowArray.map(({call,put})=>{
delete call[""]
delete put[""]
return [call,put]
}))
Insert cell
parseRowArray = option_rows
.map(x=>{
let call={},
put={};
let c=d3.zip( x.slice(0,strikePosition+1),callColumns).forEach(x=> {
call[x[1]]=x[0]
})
call=parseOption(call);
// let call = [x.slice(0,29),callColumns].reduce(function(m,v){m[v.key] = v.value; return m;}, {});
// .reduce(function(m,v){m[v.key] = v.value; return m;}, {})
//
let p= d3.zip( x.slice(34),putColumns).forEach(x=> {
put[x[1]]=x[0]
})
put=parseOption(put);
return {call,put}
}
)
Insert cell
parseOption=(option)=>{
option.Volume=+option.Volume;
option.BID =+option.BID;
option.ASK=+option.ASK;
option.Strike=+option.Strike;
option['Open.Int']=+option['Open.Int'];
option['Theo Price']=+option['Theo Price']
option['Delta']=+option['Delta']
option['Gamma']=+option['Gamma']
option['Vega']=+option['Vega']
option['Theta']=+option['Theta']
option['Rho']=+option['Rho']
option['LAST']=+option['LAST']
option['LX']=+option['LX']
option['LAST']=+option['LAST']
option['Mark']=+option['Mark']
option['Last']=+option['Last']
option['BS']=+option['BS']
option['AS']=+option['AS']
option['% Change']=+option['% Change']
option['ROC']=+option['ROC']
option['ROR']=+option['ROR']
return option;
}
Insert cell
getCall=rowArray=>rowArray.split(',').slice(0,strikePosition+1)
Insert cell
getPut=rowArray=>rowArray.split(',').slice(strikePosition-1)
Insert cell
option_rows=textRows
.filter(x=>x.startsWith(`,,${SYMBOL}`))
.map(d3.csvParse)
.map(x=>[...x.columns])

Insert cell
md`---
## Utilities`
Insert cell
parseNumbers=(str)=>{
if ( !str)return undefined;
let num = str.replace(/,/g,'');
return num;
}
Insert cell
volumeFormat = d3.format(',.0f')
Insert cell
shortDateFormat = d3.timeFormat('%d %b')
Insert cell
timestampFormat = d3.timeFormat('%c')
Insert cell
timeStampParser = d3.timeParse("%m/%d/%y %H:%M:%S")
Insert cell
exDivParser=d3.timeParse('%m/%d/%y')
Insert cell
generate_options_csv = ()=>option_chain_header.concat(`${option_rows.join('\n')}`)
Insert cell
dataCredentials = {
let temp = textRows[0].trim().split(' ');
let symbol = temp[6];
let date = temp[8];
let time = temp[9];
let dt = timeStampParser(date + ' ' + time);

return { Symbol: temp[6], TimeStamped: dt };
}
Insert cell
invalidValues = [""," ",'<empty>']
Insert cell
underlying_header=`LAST,LX,Net Chng,BID,BX,ASK,AX,Size,Volume,Open,High,Low`
Insert cell
option_chain_header = textRows
.filter(x => x.startsWith(',,Option Code'))[0]
.split(',')
Insert cell
//yayy my first working reduce...it's so unnecessary but a step in right direction.
option_chain_header.reduce((a,v)=>a+v+',')
Insert cell
strikePosition=option_chain_header.indexOf('Strike')
Insert cell
callColumns=option_chain_header.slice(0,strikePosition+1)
Insert cell
putColumns=option_chain_header.slice(strikePosition-1)
Insert cell
md`---
## Data`
Insert cell
md`### 1. Using a static mock-file`
Insert cell
text = await FileAttachment('2019-10-25-StockAndOptionQuoteForIBM.csv').text()
Insert cell
md`1. Split file into lines`
Insert cell
textRows=text.split('\n')
Insert cell
OptionSeries_Header = {
return { optionColumns: option_chain_header };
}
Insert cell
md`
---
## ANX`
Insert cell
// import { SalesLog, OptionSeries, DT } from '@stroked/36715851347f6241'
Insert cell
import { short_option_code, long_option_code } from '@stroked/option-code'
Insert cell
Insert cell
import { BigBoldHeading } from '@stroked/texting-titling'
Insert cell
BigBoldHeading
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more