Last active
October 26, 2023 19:46
-
-
Save exvion/edf4c0b44c9b8dcd6a193f0318a54a7f to your computer and use it in GitHub Desktop.
client on NodeJS for QuikSharp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { QuikClient } from './QuikClient.mjs' | |
const qc = new QuikClient({ | |
host: "10.211.55.3", | |
port: 34150 | |
}); | |
qc.connect(); | |
qc.on("connect", () => { | |
qc.subscribe("SPBRU_USD|SPBE") | |
.then((data) => { console.log(data) }); | |
// qc.sendTransaction(0, 0) | |
// .then((data)=> { console.log(data) }) | |
// .catch((error) => {console.log(error)}); | |
// qc.sendOrder("SPBRU_USD|SPBE", 0, 0) | |
// .then((order) => {console.log(new Date().toISOString() + " " + order.order_num) }) | |
// .catch((error) => {console.log(error)}); | |
}); | |
qc.on("onQuote", (quote) => console.log(quote); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import net from 'net'; | |
import { EventEmitter } from 'events'; | |
import iconv from 'iconv-lite'; | |
import { inspect } from 'util'; | |
export class OrderTradeFlags { | |
static Active = 0x1; | |
static Canceled = 0x2; | |
static IsSell = 0x4; | |
static IsLimit = 0x8; | |
static AllowDiffPrice = 0x10; | |
static FillOrKill = 0x20; | |
static IsMarketMakerOrSent = 0x40; | |
static IsReceived = 0x80; | |
static IsKillBalance = 0x100; | |
static Iceberg = 0x200; | |
} | |
export class QuikClient extends EventEmitter { | |
#options = {}; | |
#CRLF = '\r\n\r\n'; | |
responseClient = new net.Socket(); | |
callbackClient = new net.Socket(); | |
#chunk = ""; | |
#message = ""; | |
#transactionID = 0; | |
#correlationId = 0; | |
constructor(options = {}) { | |
super(); | |
this.#options = options; | |
this.responseClient.on('connect', () => { | |
this.log(`Connected to responseClient`); | |
this.callbackClient.on('connect', () => { | |
this.log(`Connected to callbackClient`); | |
this.emit('connect'); | |
}) | |
}); | |
this.responseClient.on('error', (error) => { | |
this.emit('error', error); | |
}); | |
this.callbackClient.on('data', (data) => { | |
this.#handleCallbackClientData(data); | |
}); | |
this.responseClient.on('data', (data) => { | |
this.#responseTask(data); | |
}); | |
this.callbackClient.on('end', () => { | |
console.log("end"); | |
}); | |
this.on("json", (json) => { | |
// console.log("json"); | |
}); | |
} | |
#responseTask(data) { | |
this.#message += iconv.decode(data, 'windows-1251'); | |
if (this.#message.indexOf("\n") <= 0) return; | |
const messages = this.#message.split("\n") | |
messages.map((message) => { | |
if (!message.trim()) return; | |
let data; | |
try { | |
data = JSON.parse(message.trim()) | |
} catch (error) { | |
console.log('Error while parsing quik data: ' + message); | |
console.log(error); | |
return; | |
} | |
if (!data) { | |
console.log('parsed empty data object') | |
return; | |
} | |
if (!data.hasOwnProperty('cmd') || !data.hasOwnProperty('data')) { | |
console.log('wrong data'); | |
return; | |
} | |
this.emit("onMessage", data); | |
}) | |
this.#message = ''; | |
} | |
#handleCallbackClientData(data) { | |
this.#chunk += iconv.decode(data, 'windows-1251'); | |
if (this.#chunk.indexOf("\n") <= 0) return; | |
const messages = this.#chunk.split("\n") | |
messages.map((message) => { | |
if (!message.trim()) return; | |
let data; | |
try { | |
data = JSON.parse(message.trim()) | |
} catch (error) { | |
console.log('Error while parsing quik data: ' + message); | |
console.log(error); | |
return; | |
} | |
if (!data) { | |
console.log('parsed empty data object') | |
return; | |
} | |
if (!data.hasOwnProperty('cmd') || !data.hasOwnProperty('data')) { | |
console.log('wrong data'); | |
return; | |
} | |
this.#handleMessage(data); | |
}) | |
this.#message = ''; | |
} | |
#handleMessage(message) { | |
switch (message.cmd) { | |
case 'OnQuote': | |
this.emit('onQuote', message.data); | |
break; | |
case 'OnTrade': | |
this.emit('onTrade', message.data); | |
break; | |
case 'OnParam': | |
this.emit('onParam', message.data); | |
break; | |
case 'OnOrder': | |
this.emit('onOrder', message.data); | |
break; | |
case 'OnTransReply': | |
this.emit('onTransReply', message.data); | |
break; | |
case 'OnFirm': | |
this.emit('onFirm', message.data); | |
break; | |
case 'OnDepoLimit': | |
this.emit('onDepoLimit', message.data); | |
break; | |
case 'OnAccountPosition': | |
this.emit('onAccountPosition', message.data); | |
break; | |
case 'OnMoneyLimit': | |
this.emit('onMoneyLimit', message.data); | |
break; | |
default: | |
console.log(message); | |
} | |
} | |
#getNewUniqueId() { | |
this.#correlationId++; | |
let newId = this.#correlationId; | |
if (newId > 0) { | |
return newId; | |
} | |
this.#correlationId = 1; | |
return 1; | |
} | |
#send(request) { | |
request.id = this.#getNewUniqueId(); | |
return new Promise((resolve, reject) => { | |
let raw_data = JSON.stringify(request) + this.#CRLF; | |
this.responseClient.write(raw_data); | |
this.on('onMessage', (message) => { | |
if (message["id"] === request.id) { | |
resolve(message); | |
} | |
}); | |
this.responseClient.on('error', (err) => { | |
reject(err); | |
}); | |
}); | |
} | |
getQuoteLevel2(symbol) { | |
return this.#send({ "data": symbol, "cmd": "GetQuoteLevel2", "t": "" }); | |
} | |
subscribe(symbol) { | |
return this.#send({ "data": symbol, "cmd": "Subscribe_Level_II_Quotes", "t": "" }); | |
} | |
getOrders() { | |
return this.#send({ "data": "SPBRU_USD|SPBE", "cmd": "get_orders", "t": "" }); | |
} | |
getUniqueTransactionId() { | |
return ++this.#transactionID; | |
} | |
sendTransaction(classCode, secCode, price, volume) { | |
let id = this.#getNewUniqueId(); | |
let transId = this.getUniqueTransactionId().toString(); | |
let request = { "id": id, "cmd": "sendTransaction", "t": "" }; | |
request.data = { | |
ACTION: "NEW_ORDER", | |
CLASSCODE: classCode, | |
SECCODE: secCode, | |
ACCOUNT: "SP01-CL00000", | |
CLIENT_CODE: "123456", | |
OPERATION: "B", | |
PRICE: price.toString(), | |
QUANTITY: volume.toString(), | |
TRANS_ID: transId, | |
EXECUTION_CONDITION: "PUT_IN_QUEUE" | |
} | |
let raw_data = JSON.stringify(request) + this.#CRLF; | |
return new Promise((resolve, reject) => { | |
this.responseClient.write(raw_data); | |
this.on('onMessage', (message) => { | |
console.log(message); | |
if (message["id"] === id) { | |
if (message["lua_error"] !== undefined) { | |
reject(message["lua_error"]); | |
} | |
} | |
}); | |
this.on("onTransReply", (transReply) => { | |
if (transReply.trans_id == transId && transReply.status == 6) { //Скорректированное значение НПР1 -68276.05 (RUB) меньше 0 | |
reject(transReply.result_msg); | |
} else { | |
resolve(transId); | |
} | |
}) | |
this.responseClient.on('error', (err) => { | |
reject(err); | |
}); | |
}); | |
} | |
ping() { | |
console.log("pong"); | |
} | |
//symbol = "SPBXM|TCS" | |
sendOrder(symbol, price, volume) { | |
let [classCode, secCode] = symbol.split('|', 2); | |
console.log(secCode + " " + secCode); | |
return new Promise((resolve, reject) => { | |
this.sendTransaction(classCode, secCode, price, volume) | |
.then((transId) => { | |
this.on('onOrder', (order) => { | |
if (order.trans_id == transId) { | |
order.flags & OrderTradeFlags.Active | |
? console.log("active") | |
: (order.flags & OrderTradeFlags.Canceled | |
? console.log("cancelled") : console.log("completed") | |
) | |
console.log(order.flags); | |
resolve(order); | |
} | |
}) | |
}) | |
.catch((error) => { | |
reject(error); | |
}); | |
}) | |
} | |
connect() { | |
this.log(`Connecting to ${this.#options.host}:${this.#options.port}...`); | |
this.responseClient.connect(this.#options.port, this.#options.host); | |
this.responseClient.on('connect', () => { | |
this.callbackClient.connect(this.#options.port + 1, this.#options.host); | |
}); | |
} | |
log(message) { | |
console.log(inspect(message, { depth: 10 })); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment