-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathorder.go
252 lines (232 loc) · 9.14 KB
/
order.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
package go5paisa
import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"strconv"
"time"
)
// OrderValidity is the enum type
type OrderValidity int
const (
//Day is valid for just a single day
Day OrderValidity = 0
// GTD is a type of order that is active until its specified date, unless it has already been fulfilled or cancelled.
GTD = 1
// GTC stays open until it is canceled
GTC = 2
// IOC is an order to buy or sell a security that attempts to execute all or part immediately and then cancels any unfilled portion of the order
IOC = 3
// EOS is exclusive to the BSE segment. It is an order to buy or sell a security that automatically expires, if not executed by 3:30 pm.
EOS = 4
// FOK is to execute a transaction immediately and completely or not at all
FOK = 6
)
// Exchanges
const (
NSE = "N"
BSE = "B"
MCX = "M"
)
// Exchange Segments
const (
CASH = "C"
DERIVATIVE = "D"
CURRENCY = "U"
)
//Order Types
const (
BUY = "BUY"
SELL = "MSELL"
)
//Order placed after hours
const (
AFTERMARKET = "Y"
NORMAL = "N"
)
// Order to be placed
type Order struct {
ClientCode string `json:"ClientCode"` // Client code for whom order is being placed.
OrderFor string `json:"OrderFor"` // P- Place (Fresh ), M-Modify, C- Cancel Order
Exchange string `json:"Exchange"` // Exchange in which order has been placed. N- NSE, B- BSE, M-MCX
ExchangeSegment string `json:"ExchangeType"` // Exchange segment. C-Cash, D-Derivative, U - Currency
Price float32 `json:"Price"` // Rate at which client wants to Buy / Sell the stock.(Price=0 for at market order)
OrderID int `json:"OrderID"` // It is an incremental number for each order after login.
OrderType string `json:"OrderType"` // Client want to buy or sell Buy/MSell
Qty int `json:"Qty"` // Total quantity client want to buy or sell (In Case Of Derivative Send Market Lot in Qty)
OrderDateTime string `json:"OrderDateTime"` // Local date at which order has been placed.
ScripCode int `json:"ScripCode"` // Scrip Code of the requested order.
AtMarket bool `json:"AtMarket"` // Specifies where the order placed is at market or Limit Order. Send False for Limit Order and True for At Market Order.
RemoteOrderID string `json:"RemoteOrderID"` // This will be unique ID for each order created by you.
ExchangeOrderID int `json:"ExchOrderID"` // This is order reference number generated by exchange for an order. Send 0 for fresh order and for modify cancel send the exchange order id received from exchange.
DisclosedQty int `json:"DisQty"` // Quantity exposed in the exchange. Disclosed quantity is never larger than order quantity.
IsStopLossOrder bool `json:"IsStopLossOrder"` // This will be the trigger price. This will be set when user want to place stop loss order. (For Buy Stop loss, Trigger price should not be greater than Limit Price. And for Sell Stop Loss Order Trigger Price should not be less than Limit Price)
IsVTD bool `json:"IsVTD"` // True=Valid for day
IsIOCOrder bool `json:"IOCOrder"` // Send False in case order is not IOC
IsIntraday bool `json:"IsIntraday"` // For Intraday order send this flag as True and for Delivery order send it as False
PublicIP string `json:"PublicIP"`
AHPlaced string `json:"AHPlaced"` // Y-in case order placed after market closed whereas N- in case of Normal Market time Order
ValidTillDate string `json:"ValidTillDate"` // Order Validity Date For VTD (GTD in Case of Commodity) Order should not be today’s or earlier date. Date should be in Range of 45 days from next Day
OrderValidity OrderValidity `json:"iOrderValidity"` // Enum of OrderValidity
TradedQty int `json:"TradedQty"` // Pass the traded qty. For placing fresh order, value should be 0. For modification/cancellation, send the actual traded qty. Incorrect value would lead to order rejection.
OrderRequesterCode string `json:"OrderRequesterCode"` // Client code
AppSource int64 `json:"AppSource"` // App source generated at the time of registration
}
type orderPayload struct {
Head *payloadHead `json:"head"`
Body *Order `json:"body"`
}
type RMSResponse struct {
ClientCode string `json:"ClientCode"`
BrokerOrderID int32 `json:"BrokerOrderID"`
LocalOrderID int16 `json:"LocalOrderID"`
ExchangeOrderID string `json:"ExchOrderID"`
Exchange string `json:"Exch"`
ExchangeType string `json:"ExchType"`
RMSResponseCode int `json:"RMSResponseCode"`
Status int `json:"Status"`
Message string `json:"Message"`
Time string `json:"Time"`
ScripCode int `json:"ScripCode"`
}
func setDefaults(order *Order, c *Client) error {
switch {
// TODO: stronger check
case order.Exchange == "":
return &InvalidOrderError{
Err: errors.New("Invalid exchange, valid exchange types are NSE, BSE and MCX "),
}
case order.OrderType == "":
return &InvalidOrderError{
Err: errors.New("Invalid Order type, valid order types are BUY and MSELL"),
}
case order.Qty == 0:
return &InvalidOrderError{
Err: errors.New("Invalid qty"),
}
case order.ScripCode == 0:
return &InvalidOrderError{
Err: errors.New("Invalid scrip code"),
}
default:
if order.ExchangeSegment == "" {
order.ExchangeSegment = CASH
}
if order.OrderFor == "P" {
order.TradedQty = 0
order.ExchangeOrderID = 0
}
if order.AHPlaced == "" {
order.AHPlaced = NORMAL
}
if order.AtMarket == false && order.Price == 0 {
order.AtMarket = true
}
order.ClientCode = c.clientCode
order.OrderRequesterCode = c.clientCode
order.PublicIP = "192.168.1.1"
order.AppSource, _ = strconv.ParseInt(c.appConfig.config.AppSource, 10, 64)
now := time.Now()
timestamp := strconv.FormatInt(now.UTC().Unix(), 10)
next := now.AddDate(0, 0, 1).UTC().Unix()
order.OrderDateTime = "/Date(" + timestamp + ")/"
order.ValidTillDate = "/Date(" + strconv.FormatInt(next, 10) + ")/"
order.RemoteOrderID = "1"
}
return nil
}
func setDefaultsModifyOrCancel(order *Order, c *Client) error {
switch {
case order.ExchangeOrderID == 0:
return &InvalidOrderError{
Err: errors.New("Please specify exchange order ID to modify/cancel order"),
}
case order.Exchange == "":
return &InvalidOrderError{
Err: errors.New("Please specify the exchange. Valid exchange types are NSE, BSE and MCX "),
}
case order.TradedQty == 0:
return &InvalidOrderError{
Err: errors.New("Please specify the traded quantity to modify/cancel order"),
}
case order.ScripCode == 0:
return &InvalidOrderError{
Err: errors.New("Please specify the scrip code to modify/cancel order"),
}
default:
if order.AHPlaced == "" {
order.AHPlaced = NORMAL
}
if order.AtMarket == false && order.Price == 0 {
order.AtMarket = true
}
if order.ExchangeSegment == "" {
order.ExchangeSegment = CASH
}
order.Qty = order.TradedQty
order.ClientCode = c.clientCode
order.OrderRequesterCode = c.clientCode
order.PublicIP = "192.168.1.1"
order.AppSource, _ = strconv.ParseInt(c.appConfig.config.AppSource, 10, 64)
now := time.Now()
timestamp := strconv.FormatInt(now.UTC().Unix(), 10)
next := now.AddDate(0, 0, 1).UTC().Unix()
order.OrderDateTime = "/Date(" + timestamp + ")/"
order.ValidTillDate = "/Date(" + strconv.FormatInt(next, 10) + ")/"
order.RemoteOrderID = "1"
}
return nil
}
func orderRequest(rmsResponse RMSResponse, order *Order, c *Client) (RMSResponse, error) {
c.appConfig.head.RequestCode = orderPlacementRequestCode
payload := orderPayload{
Head: c.appConfig.head,
Body: order,
}
jsonValue, _ := json.Marshal(payload)
res, err := c.connection.Post(baseURL+orderPlacementRoute, contentType, bytes.NewBuffer(jsonValue))
if err != nil {
return rmsResponse, err
}
defer res.Body.Close()
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return rmsResponse, err
}
parseResBody(resBody, &rmsResponse)
if rmsResponse.RMSResponseCode != 0 {
return rmsResponse, errors.New(rmsResponse.Message)
}
return rmsResponse, nil
}
// PlaceOrder places an order
func (c *Client) PlaceOrder(order Order) (RMSResponse, error) {
var rmsResponse RMSResponse
order.OrderFor = "P"
err := setDefaults(&order, c)
if err != nil {
return rmsResponse, err
}
return orderRequest(rmsResponse, &order, c)
}
// ModifyOrder modifies an order
func (c *Client) ModifyOrder(order Order) (RMSResponse, error) {
var rmsResponse RMSResponse
order.OrderFor = "M"
err := setDefaultsModifyOrCancel(&order, c)
if err != nil {
return rmsResponse, err
}
return orderRequest(rmsResponse, &order, c)
}
// CancelOrder cancels an order
func (c *Client) CancelOrder(order Order) (RMSResponse, error) {
var rmsResponse RMSResponse
order.OrderFor = "C"
err := setDefaultsModifyOrCancel(&order, c)
if err != nil {
return rmsResponse, err
}
return orderRequest(rmsResponse, &order, c)
}