Compare commits
10 Commits
b8b99a8a9e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 88abb2c21c | |||
| 030ccf23cf | |||
| 684e217f74 | |||
| e61c190a2f | |||
| 8fab62bd32 | |||
| 548591c6e6 | |||
| 390b60b8d8 | |||
| 1f2ccc783e | |||
| aabc2d235e | |||
| bbb9d5dce6 |
@@ -23,6 +23,9 @@
|
|||||||
{
|
{
|
||||||
"Symbol": "DIS"
|
"Symbol": "DIS"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Symbol": "DOW"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Symbol": "EDV"
|
"Symbol": "EDV"
|
||||||
},
|
},
|
||||||
@@ -71,24 +74,42 @@
|
|||||||
{
|
{
|
||||||
"Symbol": "META"
|
"Symbol": "META"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Symbol": "MSTR"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Symbol": "NFLX"
|
"Symbol": "NFLX"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Symbol": "NVDA"
|
"Symbol": "NVDA"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Symbol": "PG"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Symbol": "PM"
|
"Symbol": "PM"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Symbol": "PLTR"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Symbol": "PZZA"
|
"Symbol": "PZZA"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Symbol": "QQQ"
|
"Symbol": "QQQ"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Symbol": "RCL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Symbol": "REMX"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Symbol": "SLV"
|
"Symbol": "SLV"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Symbol": "SPCX"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Symbol": "SPY"
|
"Symbol": "SPY"
|
||||||
},
|
},
|
||||||
@@ -98,6 +119,9 @@
|
|||||||
{
|
{
|
||||||
"Symbol": "TSLA"
|
"Symbol": "TSLA"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Symbol": "VGT"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Symbol": "$VIX"
|
"Symbol": "$VIX"
|
||||||
},
|
},
|
||||||
|
|||||||
130
autoTradeGenericLongCall.sh
Executable file
130
autoTradeGenericLongCall.sh
Executable file
@@ -0,0 +1,130 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#set -x
|
||||||
|
IFS=$'\0'
|
||||||
|
|
||||||
|
. helpers.sh
|
||||||
|
|
||||||
|
|
||||||
|
msg=$(echo "Called with parameters: _$@_")
|
||||||
|
log "$msg" info
|
||||||
|
|
||||||
|
Symbol="$1"
|
||||||
|
if [[ -z ${Symbol} ]]; then
|
||||||
|
throw "A Stock symbol must be given as first parameter. exiting." err 2
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
AccountName="$1"
|
||||||
|
if [[ -z ${AccountName} ]]; then
|
||||||
|
throw "An Account name must be given as first parameter. exiting." err 2
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
if [[ -z $(./getAccountNumbers.sh "${AccountName}") ]]; then
|
||||||
|
throw "An the account with name _${AccountName}_ doesn't exist. exiting." err 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
marketOpen=$([[ $(./getMarketHours.sh | jq '.option[].isOpen' | grep true | wc -l) -ne 0 ]] && echo true || echo false)
|
||||||
|
if [[ ${marketOpen} == false ]]; then
|
||||||
|
throw "The market is closed, exiting." info 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
#Trade variables - overwrite with -VAR_<variable name>=<value>
|
||||||
|
daysOutMin=87
|
||||||
|
daysOutMax=91
|
||||||
|
deltaMin=0.35
|
||||||
|
deltaMax=0.40
|
||||||
|
priceOffset=0.0
|
||||||
|
checkPositionExists=true
|
||||||
|
|
||||||
|
#Allow to overwrite any variable by a commandline parameter with the name of the variable:
|
||||||
|
while [[ $# -gt 0 ]]
|
||||||
|
do
|
||||||
|
if [[ ${1:0:5} == "-VAR_" ]]; then
|
||||||
|
log "Setting Variable: ${1:5}" info
|
||||||
|
eval ${1:5}
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
inputFile=temp/orderInputFile.$$.json
|
||||||
|
./getOptionChain.sh ''${Symbol}'' fromDate $(date "+%Y-%m-%d" -d "+${daysOutMin} days") toDate $(date "+%Y-%m-%d" -d "+${daysOutMax} days") > "${inputFile}"
|
||||||
|
|
||||||
|
if [[ ! -f "${inputFile}" ]]; then
|
||||||
|
throw "ERROR: Input file _${inputFile}_ does not exist." err 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#Find the date closest to the Minimum number of days
|
||||||
|
dateKey=$(jq -r '.callExpDateMap | keys | .[]' "${inputFile}" | sort -r -t ":" +2 | awk -F ":" -e '{theDay=$2; if (theDay < '${daysOutMin}') { print lastDay; exit; } else { lastDay=$0; } } END { print $0 }')
|
||||||
|
log "Date-Key: _${dateKey}_" debug
|
||||||
|
|
||||||
|
daysOut=$(cut -d : -f 2 <<< ${dateKey})
|
||||||
|
if [[ ${daysOut} -gt ${daysOutMax} ]]; then
|
||||||
|
throw "This option is too far in the future. _${daysOut}_ is bigger than the max days in the future is _${daysOutMax}_. Exiting." info
|
||||||
|
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
|
||||||
|
throw "No suitable long leg found - exiting." warning 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)
|
||||||
|
log "Long Symbol: _${longSymbol}_ Strike: _${longStrike}_ Bid: _${longBid}_ Ask: _${longAsk}_" info
|
||||||
|
|
||||||
|
if [[ "${checkPositionExists}" = true ]]; then
|
||||||
|
#Check if this order has already been submitted.
|
||||||
|
./getOrders.sh | grep '\(WORKING\|FILLED\),'"${longSymbol:0:12}" > temp/existingOrders.$$
|
||||||
|
if [[ $(wc -l < temp/existingOrders.$$) -eq 0 ]];then
|
||||||
|
log "Not Yet traded, continuing" info
|
||||||
|
else
|
||||||
|
throw "Already traded, exiting" info
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the Middle price...
|
||||||
|
price=$(bc <<< "scale=2; ((${longBid}+${longAsk})/2) + ${priceOffset}")
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
log "Order: _${orderJson}_" debug
|
||||||
|
|
||||||
|
if [[ "${noconfirm}" != "true" ]]; then
|
||||||
|
echo Enter to continue, Ctrl+C to cancel _${noconfirm}_
|
||||||
|
read
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl -s -X POST \
|
||||||
|
"https://api.schwabapi.com/trader/v1/accounts/$(./getAccountNumbers.sh "${AccountName}")/orders" \
|
||||||
|
-H "Authorization: Bearer $(./getNewAccessToken.sh)" \
|
||||||
|
-H 'accept: */*' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "${orderJson}"
|
||||||
|
|
||||||
|
log "Result: _$?_" debug
|
||||||
@@ -4,7 +4,12 @@ IFS=$'\0'
|
|||||||
|
|
||||||
. helpers.sh
|
. helpers.sh
|
||||||
|
|
||||||
daysOutMin=87
|
marketOpen=$([[ $(./getMarketHours.sh | jq '.option[].isOpen' | grep true | wc -l) -ne 0 ]] && echo true || echo false)
|
||||||
|
if [[ ${marketOpen} == false ]]; then
|
||||||
|
throw "The market is closed, exiting." info 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
daysOutMin=85
|
||||||
daysOutMax=91
|
daysOutMax=91
|
||||||
deltaMin=0.35
|
deltaMin=0.35
|
||||||
deltaMax=0.40
|
deltaMax=0.40
|
||||||
@@ -44,7 +49,7 @@ longSymbol=$(jq -r '.symbol' < temp/longLeg.json)
|
|||||||
log "Long Symbol: _${longSymbol}_ Strike: _${longStrike}_ Bid: _${longBid}_ Ask: _${longAsk}_" info
|
log "Long Symbol: _${longSymbol}_ Strike: _${longStrike}_ Bid: _${longBid}_ Ask: _${longAsk}_" info
|
||||||
|
|
||||||
#Check if this order has already been submitted.
|
#Check if this order has already been submitted.
|
||||||
./getOrders.sh | grep '\(WORKING\|FILLED\),'${longSymbolC:0:12} > temp/existingOrders.$$
|
./getOrders.sh | grep '\(WORKING\|FILLED\),'"${longSymbol:0:12}" > temp/existingOrders.$$
|
||||||
if [[ $(wc -l < temp/existingOrders.$$) -eq 0 ]];then
|
if [[ $(wc -l < temp/existingOrders.$$) -eq 0 ]];then
|
||||||
log "Not Yet traded, continuing" info
|
log "Not Yet traded, continuing" info
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ if [[ "${noconfirm}" != "true" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
curl -s -X POST \
|
curl -s -X POST \
|
||||||
"https://api.schwabapi.com/trader/v1/accounts/$(./getAccountNumbers.sh Regular)/orders" \
|
"https://api.schwabapi.com/trader/v1/accounts/$(./getAccountNumbers.sh IRA)/orders" \
|
||||||
-H "Authorization: Bearer $(./getNewAccessToken.sh)" \
|
-H "Authorization: Bearer $(./getNewAccessToken.sh)" \
|
||||||
-H 'accept: */*' \
|
-H 'accept: */*' \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ priceOffset=0.00 # try to get a little better price
|
|||||||
# 2) Round to the Coloses full number
|
# 2) Round to the Coloses full number
|
||||||
# 3) Divide by 20
|
# 3) Divide by 20
|
||||||
priceOpen=$(echo "scale=2; exact=((${shortBidC}+${shortAskC})/2) + ((${shortBidP}+${shortAskP})/2) - ((${longBidC}+${longAskC})/2) - ((${longBidP}+${longAskP})/2) + ${priceOffset}; scale=0; round5=exact*20/1; scale=2; round5/20" | bc)
|
priceOpen=$(echo "scale=2; exact=((${shortBidC}+${shortAskC})/2) + ((${shortBidP}+${shortAskP})/2) - ((${longBidC}+${longAskC})/2) - ((${longBidP}+${longAskP})/2) + ${priceOffset}; scale=0; round5=exact*20/1; scale=2; round5/20" | bc)
|
||||||
priceOpen=4.80 # All nice calculating a price, but I want those to be filled and reasonably, they will (hopefully) be filled at 475
|
priceOpen=4.75 # All nice calculating a price, but I want those to be filled and reasonably, changing to 475 as 480 failed me on 2024-12-03
|
||||||
|
|
||||||
orderJson=$(jq -c <<-EOM
|
orderJson=$(jq -c <<-EOM
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ IFS=$'\0'
|
|||||||
|
|
||||||
. helpers.sh
|
. helpers.sh
|
||||||
|
|
||||||
|
marketOpen=$([[ $(./getMarketHours.sh | jq '.option[].isOpen' | grep true | wc -l) -ne 0 ]] && echo true || echo false)
|
||||||
|
if [[ ${marketOpen} == false ]]; then
|
||||||
|
throw "The market is closed, exiting." info 2
|
||||||
|
fi
|
||||||
|
|
||||||
daysOutMin=87
|
daysOutMin=87
|
||||||
daysOutMax=91
|
daysOutMax=91
|
||||||
deltaMin=0.35
|
deltaMin=0.35
|
||||||
@@ -49,7 +54,7 @@ longSymbol=$(jq -r '.symbol' < temp/longLeg.json)
|
|||||||
log "Long Symbol: _${longSymbol}_ Strike: _${longStrike}_ Bid: _${longBid}_ Ask: _${longAsk}_" info
|
log "Long Symbol: _${longSymbol}_ Strike: _${longStrike}_ Bid: _${longBid}_ Ask: _${longAsk}_" info
|
||||||
|
|
||||||
#Check if this order has already been submitted.
|
#Check if this order has already been submitted.
|
||||||
./getOrders.sh | grep '\(WORKING\|FILLED\),'${longSymbolC:0:12} > temp/existingOrders.$$
|
./getOrders.sh | grep '\(WORKING\|FILLED\),'"${longSymbol:0:12}" > temp/existingOrders.$$
|
||||||
if [[ $(wc -l < temp/existingOrders.$$) -eq 0 ]];then
|
if [[ $(wc -l < temp/existingOrders.$$) -eq 0 ]];then
|
||||||
log "Not Yet traded, continuing" info
|
log "Not Yet traded, continuing" info
|
||||||
else
|
else
|
||||||
|
|||||||
48
crontabExport.txt
Normal file
48
crontabExport.txt
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Edit this file to introduce tasks to be run by cron.
|
||||||
|
#
|
||||||
|
# Each task to run has to be defined through a single line
|
||||||
|
# indicating with different fields when the task will be run
|
||||||
|
# and what command to run for the task
|
||||||
|
#
|
||||||
|
# To define the time you can provide concrete values for
|
||||||
|
# minute (m), hour (h), day of month (dom), month (mon),
|
||||||
|
# and day of week (dow) or use '*' in these fields (for 'any').
|
||||||
|
#
|
||||||
|
# Notice that tasks will be started based on the cron's system
|
||||||
|
# daemon's notion of time and timezones.
|
||||||
|
#
|
||||||
|
# Output of the crontab jobs (including errors) is sent through
|
||||||
|
# email to the user the crontab file belongs to (unless redirected).
|
||||||
|
#
|
||||||
|
# For example, you can run a backup of all your user accounts
|
||||||
|
# at 5 a.m every week with:
|
||||||
|
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
|
||||||
|
#
|
||||||
|
# For more information see the manual pages of crontab(5) and cron(8)
|
||||||
|
|
||||||
|
# JTR: Set some environemnt variables for batch mode execution
|
||||||
|
NoConsoleLogging=true
|
||||||
|
noconfirm=true
|
||||||
|
|
||||||
|
# m h dom mon dow command
|
||||||
|
|
||||||
|
# Export crontab to have it in version control too
|
||||||
|
29 8 * * 1-5 crontab -l > ~/OptionRecorderSchwab/crontabExport.txt
|
||||||
|
|
||||||
|
# Recording
|
||||||
|
*/15 8-15 * * 1-5 cd ~/OptionRecorderSchwab && ./recordAllSymbolsSingleRun.sh
|
||||||
|
#30 10 * * 1-5 cd ~/OptionRecorderSchwab && ./record90dayOut.sh
|
||||||
|
|
||||||
|
##Auto-trading
|
||||||
|
# Weekly put credit spreads
|
||||||
|
# Paused, too many outstanding contracts 35 10 * * 4 cd ~/OptionRecorderSchwab && ./autoTradeSPXPutCreditSpread.sh
|
||||||
|
# Weekly long SPY position
|
||||||
|
#Paused, market downturn might take long... 37 10 * * 4 cd ~/OptionRecorderSchwab && ./autoTradeGenericLongCall.sh SPY Roth -VAR_checkPositionExists=flase -VAR_daysOutMax=120
|
||||||
|
# 90 day out trades as soon as those options pop up
|
||||||
|
#36 10 * * 1-5 cd ~/OptionRecorderSchwab && ./autoTradeGenericLongCall.sh QQQ IRA
|
||||||
|
#37 10 * * 1-5 cd ~/OptionRecorderSchwab && ./autoTradeGenericLongCall.sh SPY Roth
|
||||||
|
#38 10 * * 1-5 cd ~/OptionRecorderSchwab && ./autoTradeGenericLongCall.sh DIA IRA -VAR_daysOutMin=85
|
||||||
|
|
||||||
|
#1DTE SPX trading every day right after the market closes.
|
||||||
|
#59 14 * * 1-5 cd ~/OptionRecorderSchwab && ./autoTradeSPX_IB_1DTE.sh
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ fi
|
|||||||
cacheFileName=temp/marketHours.`date +%Y%m%d`.${markets//&markets=/_}.tmp
|
cacheFileName=temp/marketHours.`date +%Y%m%d`.${markets//&markets=/_}.tmp
|
||||||
|
|
||||||
if [[ -f ${cacheFileName} ]]; then
|
if [[ -f ${cacheFileName} ]]; then
|
||||||
|
log "Answering from cache file _${cacheFileName}_ query string: _${queryString}_" debug
|
||||||
jq < ${cacheFileName}
|
jq < ${cacheFileName}
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ if [[ ${ageInSec} -gt 600 ]]; then
|
|||||||
-d "grant_type=refresh_token&refresh_token=$(<refresh_token.dat)" > temp/accessTokenReqResp.$$.json
|
-d "grant_type=refresh_token&refresh_token=$(<refresh_token.dat)" > temp/accessTokenReqResp.$$.json
|
||||||
jq -e -r .access_token temp/accessTokenReqResp.$$.json > access_token.dat
|
jq -e -r .access_token temp/accessTokenReqResp.$$.json > access_token.dat
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
curl -d "" -X POST "https://deadswitch.kawomi.com/api/v1?topic=trade.schwab.error.getNewAccessToken&secs=1&email=joe@kawomi.com"
|
curl -s -d "" -X POST "https://deadswitch.kawomi.com/api/v1?topic=trade.schwab.error.getNewAccessToken&secs=1&email=joerg.tretter@gmail.com"
|
||||||
throw "ERROR: GETTING THE ACCESS TOKEN FAILED! $(<temp/accessTokenReqResp.$$.json)" err
|
throw "ERROR: GETTING THE ACCESS TOKEN FAILED! $(<temp/accessTokenReqResp.$$.json)" err
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ done
|
|||||||
|
|
||||||
if [[ -e temp/hadErrors.$$.tmp ]]; then
|
if [[ -e temp/hadErrors.$$.tmp ]]; then
|
||||||
rm temp/hadErrors.$$.tmp
|
rm temp/hadErrors.$$.tmp
|
||||||
curl -d "" -X POST "https://deadswitch.kawomi.com/api/v1?topic=trade.schwab.error.recordAllSymbolsSingleRun&secs=1&email=joe@kawomi.com"
|
curl -s -d "" -X POST "https://deadswitch.kawomi.com/api/v1?topic=trade.schwab.error.recordAllSymbolsSingleRun&secs=1&email=joerg.tretter@gmail.com"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Finished"
|
log "Finished"
|
||||||
|
|||||||
Reference in New Issue
Block a user