From e9a651f92e12369bc0dbb541e958c11800b55053 Mon Sep 17 00:00:00 2001 From: Joe Tretter Date: Wed, 17 Jul 2024 14:08:55 -0500 Subject: [PATCH] - Fix SPX auto trading by differntiation of SPX and SPWX - Prevent destruciton of the refresh token when things go wrong - add auto trading wip code for order getting, and long trading - add recording of 90 days out expiration tracking - Add TSLA --- .gitignore | 1 + allSymbols.json | 3 ++ autoTradeQQQLongCall.sh | 92 ++++++++++++++++++++++++++++++++++ autoTradeSPXPutCreditSpread.sh | 7 ++- autoTradeSPYLongCall.sh | 92 ++++++++++++++++++++++++++++++++++ getNewRefreshToken.sh | 16 +++++- getOrders.sh | 13 +++++ record90dayOut.sh | 32 ++++++++++++ recordAllSymbolsSingleRun.sh | 4 +- 9 files changed, 255 insertions(+), 5 deletions(-) create mode 100755 autoTradeQQQLongCall.sh create mode 100755 autoTradeSPYLongCall.sh create mode 100755 getOrders.sh create mode 100755 record90dayOut.sh diff --git a/.gitignore b/.gitignore index 36bc71d..b0fe0de 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ appCredentials.dat refresh_token.dat appKey.dat cert.pem +*.log diff --git a/allSymbols.json b/allSymbols.json index b5fc96c..e930bd1 100644 --- a/allSymbols.json +++ b/allSymbols.json @@ -95,6 +95,9 @@ { "Symbol": "TLT" }, + { + "Symbol": "TSLA" + }, { "Symbol": "$VIX" }, diff --git a/autoTradeQQQLongCall.sh b/autoTradeQQQLongCall.sh new file mode 100755 index 0000000..a8fd341 --- /dev/null +++ b/autoTradeQQQLongCall.sh @@ -0,0 +1,92 @@ +#!/bin/bash +set -x +IFS=$'\0' + +daysOutMin=90 +daysOutMax=91 +deltaMin=0.35 +deltaMax=0.40 + +inputFile="$1" +if [[ "" == "$inputFile" ]]; then + latestFile="$(ls data/`date +%Y-%m-%d`/*QQQ* | sort | tail -n 1)" + systemd-cat -t "`basename $0` $1" -p debug <<< "Latest File: _${latestFile}_" + inputFile=temp/orderInputFile.json + bzip2 -dc < "${latestFile}" > "${inputFile}" +fi +if [[ ! -f "${inputFile}" ]]; then + errMsg="ERROR: Input file _${inputFile}_ does not exist." + systemd-cat -t "`basename $0` $1" -p err <<< ${errMsg} + >&2 echo "${errMsg}" + exit 1 +fi + +dateKey=$(jq -r '.callExpDateMap | keys | .[]' temp/orderInputFile.json | sort -r -t ":" +2 | awk -F ":" -e '{theDay=$2; if (theDay < '${daysOutMin}') { print lastDay; exit; } else { lastDay=$0; } }') +systemd-cat -t "`basename $0` $1" -p debug <<< "Date-Key: _${dateKey}_" + +daysOut=$(cut -d : -f 2 <<< ${dateKey}) +if [[ ${daysOut} -gt ${daysOutMax} ]]; then + systemd-cat -t "`basename $0` $1" -p info <<< "This option is too far in the future. _${daysOut}_ is bigger than the max days in the future is _${daysOutMax}_. Exiting." + exit +fi + +#Looking for leg, between the specified deltas +# JTR - the "floor" is to only find the prices at the integer strikes, but I changed my mind about that... +#jq '.callExpDateMap."'${dateKey}'"[]|map({symbol, delta, strikePrice,bid,ask}) | .[] | select(.delta >= '${deltaMin}' and .delta <= '${deltaMax}') | select( (.strikePrice | floor) == .strikePrice)' "${inputFile}" | tail -n 7 > temp/longLeg.json +jq '.callExpDateMap."'${dateKey}'"[]|map({symbol, delta, strikePrice,bid,ask}) | .[] | select(.delta >= '${deltaMin}' and .delta <= '${deltaMax}')' "${inputFile}" | tail -n 7 > temp/longLeg.json +numLinesInLeg=$(wc -l temp/longLeg.json | cut -c1) +if [[ 7 -ne ${numLinesInLeg} ]]; then + errMsg="No suitable long leg found - exiting." + systemd-cat -t "`basename $0` $1" -p warning <<< ${errMsg} + >&2 echo "${errMsg}" + exit 1 +fi + +longStrike=$(jq -r '.strikePrice' < temp/longLeg.json) +longBid=$(jq -r '.bid' < temp/longLeg.json) +longAsk=$(jq -r '.ask' < temp/longLeg.json) +longSymbol=$(jq -r '.symbol' < temp/longLeg.json) +msg="Long Symbol: _${longSymbol}_ Strike: _${longStrike}_ Bid: _${longBid}_ Ask: _${longAsk}_" +systemd-cat -t "`basename $0` $1" -p info <<< ${msg} +echo "${msg}" + +priceOffset=0.0 +# Get the Middle price... +price=$(echo "scale=2; ((${longBid}+${longAsk})/2) + ${priceOffset}" | bc) + +orderJson=$(jq -c <<-EOM +{ + "orderType": "LIMIT", + "session": "NORMAL", + "price": ${price}, + "duration": "DAY", + "orderStrategyType": "SINGLE", + "quantity": 1, + "orderLegCollection": [ + { + "instruction": "BUY_TO_OPEN", + "quantity": 1, + "instrument": { + "symbol": "${longSymbol}", + "assetType": "OPTION" + } + } + ] +} +EOM +) + + +systemd-cat -t "`basename $0` $1" -p debug <<< "Order: _${orderJson}_" + +echo Enter to continue, Ctrl+C to cancel +read +./getNewAccessToken.sh + +curl -s -X POST \ + 'https://api.schwabapi.com/trader/v1/accounts/CEF970366D4CC9553B5E8846E35FD92A1867F3E73C1082A52647256F42E4746B/orders' \ + -H "Authorization: Bearer $(= -0.30 and .delta <= -0.25) ' "${inputFile}" | tail -n 7 > temp/shortLeg.json +jq '.putExpDateMap."'${dateKey}'"[]|map({symbol,delta,strikePrice,bid,ask,optionRoot}) |.[] | select(.delta >= -0.30 and .delta <= -0.25) ' "${inputFile}" | tail -n 8 > temp/shortLeg.json shortStrike=$(jq -r '.strikePrice' < temp/shortLeg.json) shortBid=$(jq -r '.bid' < temp/shortLeg.json) shortAsk=$(jq -r '.ask' < temp/shortLeg.json) shortSymbol=$(jq -r '.symbol' < temp/shortLeg.json) +shortOptionRoot=$(jq -r '.optionRoot' < temp/shortLeg.json) msg="Short Symbol: _${shortSymbol}_ Strike: _${shortStrike}_ Bid: _${shortBid}_ Ask: _${shortAsk}_" systemd-cat -t "`basename $0` $1" -p info <<< ${msg} echo "${msg}" -jq '.putExpDateMap."'${dateKey}'"[]|map({symbol, delta, strikePrice,bid,ask}) | .[] | select(.strikePrice >= '${shortStrike}'-50 and .strikePrice < '${shortStrike}')' temp/orderInputFile.json | head -n 7 > temp/longLeg.json +#jq '.putExpDateMap."'${dateKey}'"[]|map({symbol, delta, strikePrice,bid,ask}) | .[] | select(.strikePrice >= '${shortStrike}'-50 and .strikePrice < '${shortStrike}')' temp/orderInputFile.json | head -n 7 > temp/longLeg.json +jq '.putExpDateMap."'${dateKey}'"[]|map({symbol,delta,strikePrice,bid,ask,optionRoot}) | .[] | select(.optionRoot == "'${shortOptionRoot}'" and .strikePrice >= '${shortStrike}'-50 and .strikePrice < '${shortStrike}')' temp/orderInputFile.json | head -n 8 > temp/longLeg.json longStrike=$(jq -r '.strikePrice' < temp/longLeg.json) longBid=$(jq -r '.bid' < temp/longLeg.json) longAsk=$(jq -r '.ask' < temp/longLeg.json) diff --git a/autoTradeSPYLongCall.sh b/autoTradeSPYLongCall.sh new file mode 100755 index 0000000..e4b82a1 --- /dev/null +++ b/autoTradeSPYLongCall.sh @@ -0,0 +1,92 @@ +#!/bin/bash +set -x +IFS=$'\0' + +daysOutMin=90 +daysOutMax=91 +deltaMin=0.35 +deltaMax=0.40 + +inputFile="$1" +if [[ "" == "$inputFile" ]]; then + latestFile="$(ls data/`date +%Y-%m-%d`/*SPY* | sort | tail -n 1)" + systemd-cat -t "`basename $0` $1" -p debug <<< "Latest File: _${latestFile}_" + inputFile=temp/orderInputFile.json + bzip2 -dc < "${latestFile}" > "${inputFile}" +fi +if [[ ! -f "${inputFile}" ]]; then + errMsg="ERROR: Input file _${inputFile}_ does not exist." + systemd-cat -t "`basename $0` $1" -p err <<< ${errMsg} + >&2 echo "${errMsg}" + exit 1 +fi + +dateKey=$(jq -r '.callExpDateMap | keys | .[]' temp/orderInputFile.json | sort -r -t ":" +2 | awk -F ":" -e '{theDay=$2; if (theDay < '${daysOutMin}') { print lastDay; exit; } else { lastDay=$0; } }') +systemd-cat -t "`basename $0` $1" -p debug <<< "Date-Key: _${dateKey}_" + +daysOut=$(cut -d : -f 2 <<< ${dateKey}) +if [[ ${daysOut} -gt ${daysOutMax} ]]; then + systemd-cat -t "`basename $0` $1" -p info <<< "This option is too far in the future. _${daysOut}_ is bigger than the max days in the future is _${daysOutMax}_. Exiting." + exit +fi + +#Looking for leg, between the specified deltas +# JTR - the "floor" is to only find the prices at the integer strikes, but I changed my mind about that... +#jq '.callExpDateMap."'${dateKey}'"[]|map({symbol, delta, strikePrice,bid,ask}) | .[] | select(.delta >= '${deltaMin}' and .delta <= '${deltaMax}') | select( (.strikePrice | floor) == .strikePrice)' "${inputFile}" | tail -n 7 > temp/longLeg.json +jq '.callExpDateMap."'${dateKey}'"[]|map({symbol, delta, strikePrice,bid,ask}) | .[] | select(.delta >= '${deltaMin}' and .delta <= '${deltaMax}')' "${inputFile}" | tail -n 7 > temp/longLeg.json +numLinesInLeg=$(wc -l temp/longLeg.json | cut -c1) +if [[ 7 -ne ${numLinesInLeg} ]]; then + errMsg="No suitable long leg found - exiting." + systemd-cat -t "`basename $0` $1" -p warning <<< ${errMsg} + >&2 echo "${errMsg}" + exit 1 +fi + +longStrike=$(jq -r '.strikePrice' < temp/longLeg.json) +longBid=$(jq -r '.bid' < temp/longLeg.json) +longAsk=$(jq -r '.ask' < temp/longLeg.json) +longSymbol=$(jq -r '.symbol' < temp/longLeg.json) +msg="Long Symbol: _${longSymbol}_ Strike: _${longStrike}_ Bid: _${longBid}_ Ask: _${longAsk}_" +systemd-cat -t "`basename $0` $1" -p info <<< ${msg} +echo "${msg}" + +priceOffset=0.0 +# Get the Middle price... +price=$(echo "scale=2; ((${longBid}+${longAsk})/2) + ${priceOffset}" | bc) + +orderJson=$(jq -c <<-EOM +{ + "orderType": "LIMIT", + "session": "NORMAL", + "price": ${price}, + "duration": "DAY", + "orderStrategyType": "SINGLE", + "quantity": 1, + "orderLegCollection": [ + { + "instruction": "BUY_TO_OPEN", + "quantity": 1, + "instrument": { + "symbol": "${longSymbol}", + "assetType": "OPTION" + } + } + ] +} +EOM +) + + +systemd-cat -t "`basename $0` $1" -p debug <<< "Order: _${orderJson}_" + +echo Enter to continue, Ctrl+C to cancel +read +./getNewAccessToken.sh + +curl -s -X POST \ + 'https://api.schwabapi.com/trader/v1/accounts/5A5B921917D97B89FDA53E1E1D13D2EB11E488ADA20A20B3C787477DE59A770E/orders' \ + -H "Authorization: Bearer $( cert.pem echo echo OPEN URL: "https://api.schwabapi.com/v1/oauth/authorize?client_id=$( temp/refreshTokenReqResp.$$.json -jq -r .refresh_token temp/refreshTokenReqResp.$$.json > refresh_token.dat + +jq -e -r .refresh_token temp/refreshTokenReqResp.$$.json > temp/refresh_token.$$.dat +if [[ $? -eq 0 ]]; then + errMsg="New Refresh Token created." + systemd-cat -t "`basename $0`" -p info <<< ${errMsg} + echo "${errMsg}" + cp temp/refresh_token.$$.dat refresh_token.dat +else + errMsg="Error getting new refresh token _$(&2 echo "${errMsg}" +fi echo Refresh Token: $( ${tempFile} +cat ${tempFile} | jq -r 'map(.enteredTime)[]' + diff --git a/record90dayOut.sh b/record90dayOut.sh new file mode 100755 index 0000000..8f46a1b --- /dev/null +++ b/record90dayOut.sh @@ -0,0 +1,32 @@ +#!/bin/bash +#set -x +IFS=$'\0' + +daysOutMin=90 +daysOutMax=91 + +inputFile="$1" +latestFile="$1" +if [[ "" == "$inputFile" ]]; then + latestFile="$(ls data/`date +%Y-%m-%d`/*SPY* | sort | tail -n 1)" + systemd-cat -t "`basename $0` $1" -p debug <<< "Latest File: _${latestFile}_" + inputFile=temp/orderInputFile.json + bzip2 -dc < "${latestFile}" > "${inputFile}" +fi +if [[ ! -f "${inputFile}" ]]; then + errMsg="ERROR: Input file _${inputFile}_ does not exist." + systemd-cat -t "`basename $0` $1" -p err <<< ${errMsg} + >&2 echo "${errMsg}" + exit 1 +fi + +dateKey=$(jq -r '.callExpDateMap | keys | .[]' temp/orderInputFile.json | sort -r -t ":" +2 | awk -F ":" -e '{theDay=$2; if (theDay < '${daysOutMin}') { print lastDay; exit; } else { lastDay=$0; } }') +systemd-cat -t "`basename $0` $1" -p debug <<< "Date-Key: _${dateKey}_" + +daysOut=$(cut -d : -f 2 <<< ${dateKey}) +if [[ ${daysOut} -gt ${daysOutMax} ]]; then + exit +fi + +echo "$(date +%Y-%m-%d): ${dateKey} (${latestFile})" >> record90dayOut.log + diff --git a/recordAllSymbolsSingleRun.sh b/recordAllSymbolsSingleRun.sh index ee03ad8..9139604 100755 --- a/recordAllSymbolsSingleRun.sh +++ b/recordAllSymbolsSingleRun.sh @@ -20,7 +20,7 @@ jq -r '.[].Symbol' allSymbols.json | sort | while read theSymbol; do ./recordOptionChainSingleSymbolSingleRun.sh ${theSymbol} toDate $(date "+%Y-%m-%d" -d "+200 days") if [[ $? -ne 0 ]]; then systemd-cat -t "`basename $0`" -p warning <<< "falling back to stitching for ${theSymbol}" - ./recordOptionChainSingleSymbolSpecialRun.sh ${theSymbol} + ./recordOptionChainSingleSymbolSpecialRun.sh ${theSymbol} toDate $(date "+%Y-%m-%d" -d "+400 days") if [[ $? -ne 0 ]]; then touch temp/hadErrors.$$.tmp fi @@ -43,7 +43,7 @@ jq -r '.[].Symbol' allSymbols.json | sort | while read theSymbol; do fi if [[ '$SPX' = ${theSymbol} ]]; then - ./recordOptionChainSingleSymbolSingleRun.sh ${theSymbol} fromDate $(date "+%Y-%m-%d" -d "+40 days") toDate $(date "+%Y-%m-%d" -d "+45 days") + ./recordOptionChainSingleSymbolSingleRun.sh ${theSymbol} fromDate $(date "+%Y-%m-%d" -d "+40 days") toDate $(date "+%Y-%m-%d" -d "+46 days") if [[ $? -ne 0 ]]; then touch temp/hadErrors.$$.tmp fi