diff --git a/.gitignore b/.gitignore
index dc1aa56..b6cf75d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -108,3 +108,4 @@ dist
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
server/quoteDBs/consolidated.sqlite3
+server/quoteDBs/processedDBs
diff --git a/client/css/optiTrainer.css b/client/css/optiTrainer.css
index a68ae55..0b266be 100644
--- a/client/css/optiTrainer.css
+++ b/client/css/optiTrainer.css
@@ -92,6 +92,40 @@ body {
z-index:1;
}
+#lbCostAmount {
+ position: absolute;
+ top:0;
+ left:100;
+ font-weight: bolder;
+ text-align: right;
+ color:crimson;
+ width: 180px;
+}
+
+#frmOrder ul li {
+ border-top:1px solid blue;
+}
+
+#frmOrder div.orderItemLine2 {
+ text-align: right;
+}
+
+#frmOrder ul li .totalCost {
+ font-weight: bolder;
+ color: blue;
+}
+
+#frmOrder span.quantity {
+ font-weight: bolder;
+ color: blue;
+}
+#frmOrder span.openType {
+ font-weight: bolder;
+ color: blue;
+}
+#frmOrder span.singlePrice {
+ color: blue;
+}
#datePicker {
background-color:#ccff00;
position:fixed;
@@ -123,6 +157,13 @@ body {
color:#0000FF;
cursor: no-drop;
}
+.plusMinus {
+ background-color: blue;
+ color:#ccff00;
+ cursor: pointer;
+ padding-bottom: 2px;
+ margin-left: 1px;
+}
.priceClick {
cursor: copy;
diff --git a/client/index.html b/client/index.html
index 500102b..77fab41 100644
--- a/client/index.html
+++ b/client/index.html
@@ -11,8 +11,9 @@
-
+
+
OptionTrainer
diff --git a/client/js/masterFilter.js b/client/js/masterFilter.js
index f9c4781..8782100 100644
--- a/client/js/masterFilter.js
+++ b/client/js/masterFilter.js
@@ -1,12 +1,10 @@
-var filterContext={};
-
let createMasterFilterFrame=()=>{
- let theFrameDiv=document.createElement("div");
- theFrameDiv.setAttribute("id","frmMasterFilter");
- theFrameDiv.className="container-fluid "
- window.document.getElementById('app').append(theFrameDiv);
+ let me=window.myMasterFilter = document.createElement("div");
+ me.setAttribute("id","frmMasterFilter");
+ me.className="container-fluid "
+ window.document.getElementById('app').append(me);
- theFrameDiv.innerHTML=`
+ me.innerHTML=`
@@ -35,141 +33,149 @@ let createMasterFilterFrame=()=>{
`;
-
- fetchSymbols();
-
- return true;
-}
-
-let beforeShowDay=(theDate)=>{
- let isoDate=theDate.toISOString().substr(0,10);
- //console.log("BeforeShow",isoDate,theDate);
-
- let isDateFound=false;
- for (let i=0;i
{
- $.get({
- url:"/getSymbols"
- }).done(response=>{
- let allSymbols=response;
- console.log(allSymbols);
- refreshSymbolList(allSymbols);
- });
-}
-
-let getCurrentSymbol=()=>{
- return(filterContext.theSymbol);
-}
-let symbolChanged=(newSymbol)=>{
- console.log("symbolChanged",newSymbol);
- filterContext.theSymbol=newSymbol
- document.title=getCurrentSymbol();
- $("#tbSymbol").text(getCurrentSymbol());
- getAvailDatesForSymbol(getCurrentSymbol()).done(_=>{
- $("#datePicker").datepicker('destroy');
- $("#datePicker").datepicker({beforeShowDay:beforeShowDay});
- if (document.getElementById("datePicker").hasHandler !== true) { // work around multiple binding of the eventhandler - it seems that destroy doesn't remove the old handler....
- $("#datePicker").datepicker().on('changeDate', (e)=>{
- if (e.date !== undefined) {
- console.log("DatePicker changed date",e.date);
- filterContext.theDate=e.date.toISOString().substr(0,10);
- showTimesForDay(filterContext.theDate);
- }
- });
- document.getElementById("datePicker").hasHandler= true;
- }
-
- if (filterContext.theDate === undefined) {
- console.log("no date in contest, setting date to the last in chain.");
- filterContext.theDate=filterContext.availableDates[filterContext.availableDates.length-1].Date;
+ let _filterContext={}; // TODO: This is bad, grown design - should be replaced with a proper object oriented structure and observer pattern.
+ Object.defineProperty(me,"selectedDate",{
+ get:_=>{ return (_filterContext.theDate);}
+ })
+ Object.defineProperty(me,"selectedTime",{
+ get:_=>{ return (_filterContext.selectedTime);}
+ })
+
+ me.beforeShowDay=(theDate)=>{
+ let isoDate=theDate.toISOString().substr(0,10);
+ //console.log("BeforeShow",isoDate,theDate);
+
+ let isDateFound=false;
+ for (let i=0;i<_filterContext.availableDates.length;i++){
+ if (_filterContext.availableDates[i].Date===theDate.toISOString().substr(0,10)){
+ isDateFound=true;
+ break;
+ };
+ }
+
+ if (!isDateFound) {
+ return {enabled:false, tooltip:"No data"};
+ }
+ }
+
+ me.fetchSymbols=()=>{
+ $.get({
+ url:"/getSymbols"
+ }).done(response=>{
+ let allSymbols=response;
+ console.log(allSymbols);
+ me.refreshSymbolList(allSymbols);
+ });
+ }
+
+ me.getCurrentSymbol=()=>{
+ return(_filterContext.theSymbol);
+ }
+ me.symbolChanged=(newSymbol)=>{
+ console.log("symbolChanged",newSymbol);
+ _filterContext.theSymbol=newSymbol
+ document.title=me.getCurrentSymbol();
+ $("#tbSymbol").text(me.getCurrentSymbol());
+
+ me.getAvailDatesForSymbol(me.getCurrentSymbol()).done(_=>{
+ $("#datePicker").datepicker('destroy');
+ $("#datePicker").datepicker({beforeShowDay:me.beforeShowDay});
+ if (document.getElementById("datePicker").hasHandler !== true) { // work around multiple binding of the eventhandler - it seems that destroy doesn't remove the old handler....
+ $("#datePicker").datepicker().on('changeDate', (e)=>{
+ if (e.date !== undefined) {
+ console.log("DatePicker changed date",e.date);
+ _filterContext.theDate=e.date.toISOString().substr(0,10);
+ me.showTimesForDay(_filterContext.theDate);
+ }
+ });
+ document.getElementById("datePicker").hasHandler= true;
+ }
+
+ if (_filterContext.theDate === undefined) {
+ console.log("no date in contest, setting date to the last in chain.");
+ _filterContext.theDate=_filterContext.availableDates[_filterContext.availableDates.length-1].Date;
+ }
+
+ currentTZDate = new Date(Date.parse(_filterContext.theDate) + new Date().getTimezoneOffset()*60000);
+ console.log("setting datepicker date to ", _filterContext.theDate, currentTZDate);
+
+ $("#datePicker").datepicker('setDate',currentTZDate);
+ });
+ }
+
+ me.timeChanged=(newTime)=>{
+ if (_filterContext.selectedTime!==newTime){
+ _filterContext.selectedTime=newTime;
+ $("#tbSelectedTime").text(newTime);
+ console.log("Time changed", newTime);
}
- currentTZDate = new Date(Date.parse(filterContext.theDate) + new Date().getTimezoneOffset()*60000);
- console.log("setting datepicker date to ", filterContext.theDate, currentTZDate);
-
- $("#datePicker").datepicker('setDate',currentTZDate);
- });
-}
-
-let timeChanged=(newTime)=>{
- if (filterContext.selectedTime!==newTime){
- filterContext.selectedTime=newTime;
- $("#tbSelectedTime").text(newTime);
- console.log("Time changed", newTime);
- }
+ me.requestOptionChain();
+ }
- requestOptionChain();
-}
-
-let requestOptionChain=()=>{
- $.ajax({
- url:"/getOptionChain?Date=" + filterContext.theDate + "&Symbol=" + filterContext.theSymbol + "&Time="+filterContext.selectedTime
- }).done(response=>{
- dta=JSON.parse(response[0].Data);
- //console.log("received",dta);
- displayOptionChain();
- $("#tbUnderlyingPrice").text(dta.underlyingPrice.toLocaleString('en',{minimumFractionDigits: 2, maximumFractionDigits: 2}));
- });
-}
-
-let showTimesForDay=()=>{
- return $.ajax({
- url:"/getTimes?Date=" + filterContext.theDate + "&Symbol=" + filterContext.theSymbol
- }).done(response=>{
- let availableTimes=response;
- //console.log(availableTimes);
- let lbTime=$("#lbTime");
- lbTime.empty();
- allTimes = availableTimes;
- for (let i=0;i{
+ $.ajax({
+ url:"/getOptionChain?Date=" + _filterContext.theDate + "&Symbol=" + _filterContext.theSymbol + "&Time="+ _filterContext.selectedTime
+ }).done(response=>{
+ dta=JSON.parse(response[0].Data);
+ //console.log("received",dta);
+ displayOptionChain();
+ $("#tbUnderlyingPrice").text(dta.underlyingPrice.toLocaleString('en',{minimumFractionDigits: 2, maximumFractionDigits: 2}));
+ });
+ }
+
+ me.showTimesForDay=()=>{
+ return $.ajax({
+ url:"/getTimes?Date=" + _filterContext.theDate + "&Symbol=" + _filterContext.theSymbol
+ }).done(response=>{
+ let availableTimes=response;
+ //console.log(availableTimes);
+ let lbTime=$("#lbTime");
+ lbTime.empty();
+ allTimes = availableTimes;
+ for (let i=0;i${theTime}`))
+ lbTime.append($(``))
+
}
- //lbTime.append($(``))
- lbTime.append($(``))
+ $("#lbTime").change();
+ });
+
+ }
+
+ me.getAvailDatesForSymbol=(theSymbol)=>{
+ return $.ajax({
+ url:"/getDates?Symbol=" + theSymbol
+ }).done(response=>{
+ let availableDates=response;
+ //console.log(availableDates);
+ _filterContext.availableDates=availableDates;
+ });
+ }
+
+ me.refreshSymbolList=(allSymbols)=>{
+ let lbSymbol=$("#lbSymbol");
+ lbSymbol.empty();
+ for (let i=0;i${theSymbol}`))
+ }
+ }
- }
- $("#lbTime").change();
- });
-
-}
-
-let getAvailDatesForSymbol=(theSymbol)=>{
- return $.ajax({
- url:"/getDates?Symbol=" + theSymbol
- }).done(response=>{
- let availableDates=response;
- //console.log(availableDates);
- filterContext.availableDates=availableDates;
- });
-}
-
-let refreshSymbolList=(allSymbols)=>{
- let lbSymbol=$("#lbSymbol");
- lbSymbol.empty();
- for (let i=0;i${theSymbol}`))
- }
+ me.fetchSymbols();
+
+ return true;
}
diff --git a/client/js/optiTrainer.js b/client/js/optiTrainer.js
index 0c17ec8..b476cfc 100644
--- a/client/js/optiTrainer.js
+++ b/client/js/optiTrainer.js
@@ -1,4 +1,3 @@
-var costAmount=0;
var allCombinations;
var optionChainContext={};
@@ -12,50 +11,6 @@ $(document).ready(_=>{
$("#datePicker").datepicker({beforeShowDay:selectDataFirst});
});
-let createOrderFrame=()=>{
- let theFrameDiv=document.createElement("div");
- theFrameDiv.setAttribute("id","frmOrder");
- theFrameDiv.className="container-fluid ";
- window.document.getElementById('app').append(theFrameDiv);
-
- theFrameDiv.innerHTML=`
-
-
- Gain $0
-
- `;
-}
-
-let clearOrder=()=>{
- costAmount=0;
- $("#listOrderItems").empty();
- showCostAmount();
-}
-
-let addToOrder=(openType,optionData)=>{
- optionData=JSON.parse(optionData.replace(/'/g,'"'));
- console.log("AddToOrder",openType,optionData);
-
- let price=(openType.substr(0,1)==="b")?optionData.ask:optionData.bid;
- price=price*100;
-
- let priceEffect=price * ((openType.substr(0,1)==="b")?-1:1);
- costAmount+=priceEffect;
- showCostAmount();
- $("#listOrderItems").append(`[X] ${openType} | ${optionData.symbol} | ${price.toLocaleString('en',{minimumFractionDigits: 2, maximumFractionDigits: 2})}`);
-}
-
-let removeFromOrder=(ele,priceEffect)=>{
- costAmount-=priceEffect;
- let li=ele.parentElement;
- li.parentElement.removeChild(li);
- showCostAmount();
-}
-
-let showCostAmount=_=>{
- $("#lbCostAmount").text(costAmount.toLocaleString('en',{minimumFractionDigits: 2, maximumFractionDigits: 2}));
-}
let dta={};
@@ -90,8 +45,8 @@ let getCallArtifact=(callData,allColumns)=>{
classes += " priceClick";
artifact+=`
- ${bidFrm} |
- ${askFrm} |
+ ${bidFrm} |
+ ${askFrm} |
`;
return artifact;
}
@@ -116,8 +71,8 @@ let getPutArtifact=(putData,allColumns)=>{
classes += " priceClick";
artifact+=`
- ${bidFrm} |
- ${askFrm} |
+ ${bidFrm} |
+ ${askFrm} |
`;
return artifact;
}
diff --git a/client/js/orderFrame.js b/client/js/orderFrame.js
new file mode 100644
index 0000000..c6d32ed
--- /dev/null
+++ b/client/js/orderFrame.js
@@ -0,0 +1,95 @@
+let createOrderFrame=()=>{
+ let me = window.myOrderFrame = document.createElement("div");
+
+ me.setAttribute("id","frmOrder");
+ me.className="container-fluid ";
+ me.innerHTML=`
+
+
+ 0
+
+ `;
+ $('#app').append(me);
+
+ me.addToOrder=(openType,optionData)=>{
+ optionData=JSON.parse(optionData.replace(/'/g,'"'));
+ console.log("AddToOrder",openType,optionData);
+
+ let me=document.createElement("li");
+ me.multiplier = 100; // assume always 100 multipier
+ me.price = (openType.substr(0,1)==="b")?optionData.ask:optionData.bid;
+ me.priceEffect = ((openType.substr(0,1)==="b")?-1:1);
+ me.quantity = 1; // at this point, we add 1 by default.
+
+ me.updateNumbers=()=>{
+ let prevTotalCostAmount=me.totalCostAmount;
+ me.singleCostAmount = me.priceEffect * me.multiplier * me.price
+ me.totalCostAmount = me.singleCostAmount * me.quantity;
+
+ if ((prevTotalCostAmount !== undefined) && (prevTotalCostAmount !== me.totalCostAmount)) {
+ myOrderFrame.costAmount += (me.totalCostAmount - prevTotalCostAmount);
+ }
+ if (me.getElementsByClassName("quantity")[0] !== undefined) {
+ me.getElementsByClassName("quantity")[0].innerText = me.quantity;
+ me.getElementsByClassName("totalCost")[0].innerText = me.totalCostAmount.toLocaleString('en',{minimumFractionDigits: 0, maximumFractionDigits: 2});
+ }
+ };
+ me.updateNumbers();
+
+ me.increaseQty=_=>{
+ me.quantity += 1;
+ me.updateNumbers();
+ }
+
+ me.decreaseQty=_=>{
+ if (me.quantity>1) { // don't go below 0, if we did, we had to chnage the order type to use the correct (bid/ask) price.
+ me.quantity -= 1;
+ me.updateNumbers();
+ }
+ }
+
+ me.removeFromOrder=_=>{
+ me.quantity=0;
+ me.updateNumbers();
+ me.parentElement.removeChild(me);
+ }
+
+ me.innerHTML=`
+ [X] ${myMasterFilter.selectedDate}|${myMasterFilter.selectedTime.substr(0,5)}|${openType}@${Math.abs(me.singleCostAmount).toLocaleString('en',{minimumFractionDigits: 0, maximumFractionDigits: 2})}|${me.quantity} [+][-]
+
+ ${optionData.symbol} => ${me.totalCostAmount.toLocaleString('en',{minimumFractionDigits: 0, maximumFractionDigits: 2})}
+
+ `;
+
+ $("#listOrderItems").append(me);
+
+ // adjust the cumulation on the order frame
+ myOrderFrame.costAmount+=me.totalCostAmount;
+
+
+ }
+
+
+ me.clearOrder = ()=>{
+ me.costAmount=0;
+ $("#listOrderItems").empty();
+ }
+
+ me.showCostAmount=_=>{
+ $("#lbCostAmount").text(me.costAmount.toLocaleString('en',{minimumFractionDigits: 0, maximumFractionDigits: 2}));
+ }
+
+ let _costAmount={};
+ Object.defineProperty(me,'costAmount',{
+ get:_=>{return(_costAmount);},
+ set: (newVal)=>{
+ console.log("changed cost", newVal);
+ _costAmount=newVal;
+ me.showCostAmount();
+ }
+ });
+ // InitializeCostAmount
+ me.costAmount=0;
+}
+
diff --git a/server/start-win.bat b/server/start-win.bat
new file mode 100644
index 0000000..01f032e
--- /dev/null
+++ b/server/start-win.bat
@@ -0,0 +1 @@
+node node_modules\micro-core\bin\micro-serve index.js
\ No newline at end of file