-
Notifications
You must be signed in to change notification settings - Fork 2
/
optionQuotes.go
168 lines (133 loc) · 4.01 KB
/
optionQuotes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package flux
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/Jeffail/gabs/v2"
jsonpatch "github.com/evanphx/json-patch"
)
const (
// ProbabilityITM field
ProbabilityITM = QuoteField("PROBABILITY_ITM")
// OpenInterest field
OpenInterest = QuoteField("OPEN_INT")
)
// OptionQuoteRequestSignature is the parameter for an option quote request
type OptionQuoteRequestSignature struct {
// ticker for option chain get request
Underlying string
Exchange string
Fields []QuoteField
// filter for specific option chain to get
Filter OptionQuoteFilter
// internal use only
UniqueID string
}
// OptionQuoteFilter is the sub-parameter for filtering an option quote
type OptionQuoteFilter struct {
SeriesNames []string `json:"seriesNames"`
MaxStrike float64 `json:"maxStrike"`
MinStrike float64 `json:"minStrike"`
}
func (o *OptionQuoteRequestSignature) shortName() string {
return fmt.Sprintf("OPTIONCHAINGET#%s@%v", o.Underlying, o.Filter.SeriesNames)
}
// OptionQuoteCache is an object containing what is returned from an option quote request
type OptionQuoteCache struct {
Items []OptionQuoteItem `json:"items"`
Exchanges []string `json:"exchanges"`
Service string `json:"service"`
RequestID string `json:"requestId"`
RequestVer int `json:"requestVer"`
}
// OptionQuoteItem is a single option quote
type OptionQuoteItem struct {
Symbol string `json:"symbol"`
Values struct {
ASK float64 `json:"ASK"`
BID float64 `json:"BID"`
DELTA float64 `json:"DELTA"`
OPENINT float64 `json:"OPEN_INT"`
PROBABILITYITM float64 `json:"PROBABILITY_ITM"`
VOLUME int `json:"VOLUME"`
} `json:"values"`
}
type newOptionQuote struct {
Op string `json:"op"`
Path string `json:"path"`
Value OptionQuoteCache `json:"value"`
}
// RequestOptionQuote requests to get an option quote with the spec OptionQuoteRequestSignature
func (s *Session) RequestOptionQuote(spec OptionQuoteRequestSignature) (*OptionQuoteCache, error) {
uniqueID := fmt.Sprintf("%s-%d", spec.shortName(), s.OptionQuoteRequestVers[spec.shortName()])
spec.UniqueID = uniqueID
payload := gatewayRequestLoad{
Payload: []gatewayRequest{
{
Header: gatewayHeader{
Service: "quotes/options",
Ver: int(s.OptionQuoteRequestVers[spec.shortName()]),
ID: spec.UniqueID,
},
Params: gatewayParams{
UnderlyingSymbol: spec.Underlying,
Exchange: spec.Exchange,
Filter: &spec.Filter,
QuoteFields: spec.Fields,
},
},
},
}
s.OptionQuoteRequestVers[spec.shortName()]++
s.sendJSON(payload)
internalChannel := make(chan storedCache)
ctx, ctxCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer ctxCancel()
go func() {
for {
select {
case recvPayload := <-s.TransactionChannel:
if recvPayload.OptionQuote.RequestID == spec.UniqueID {
time.Sleep(1000 * time.Millisecond)
internalChannel <- s.CurrentState
return
}
case <-ctx.Done():
return
}
}
}()
select {
case recvPayload := <-internalChannel:
return &recvPayload.OptionQuote, nil
case <-ctx.Done():
return nil, ErrNotReceivedInTime
}
}
func (s *Session) optionQuoteHandler(msg []byte, patch *gabs.Container) {
rID := patch.Search("header", "id").String()
rID = rID[1 : len(rID)-1]
patch = patch.S("body", "patches", "0")
newState := s.CurrentState
path := patch.S("path").String()
if path == "\"\"" {
newState.OptionQuote = OptionQuoteCache{}
}
patch.Set(fmt.Sprintf("/optionQuote%s", path[1:len(path)-1]), "path")
bytesJSON, _ := patch.MarshalJSON()
patchStr := "[" + string(bytesJSON) + "]"
jspatch, err := jsonpatch.DecodePatch([]byte(patchStr))
if err != nil {
return
}
byteState, _ := json.Marshal(newState)
byteState, err = jspatch.Apply(byteState)
if err != nil {
return
}
json.Unmarshal(byteState, &newState)
newState.OptionQuote.RequestID = rID
s.CurrentState = newState
s.TransactionChannel <- s.CurrentState
}