Legere potes quid in basi KDB+, in lingua programmandi Q sint, quae vires et debilitates in praecedentibus meis sunt. et breviter in prooemio. In articulo, operam in Q efficiemus, quae notitiae advenientis amnis processura est et varias aggregationis functiones minutatim in modum "temporis reali" (i.e., tempus erit omnia computare ante proximam partem notitiarum). Praecipuum notae Q est quod lingua vector est quae non in singulis rebus operari sinit, sed cum vestitu, vestitu, vestitu et aliis rebus complexis. Linguae ut Q et propinqui K, J, APL brevitati sunt celebres. Saepe programma, quod varias tegumenta codicis in lingua familiari quasi Javae suscipit, paucis lineis in iis scribi potest. Hoc est quod in hoc articulo demonstrare volo.

introduction
KDB+ datorum columnarum in maximas notitiarum copia feruntur, certo modo (praesertim tempore). Imprimis adhibetur in institutis nummariis, ripae, pecuniae collocandae, manipulos assecurationis. Lingua Q lingua est interna KDB+ quae te cum hac notitia efficaciter operari sinit. In Q doetrina brevitas et efficacia est, et claritas immolatur. Hoc iustificatur ex eo quod lingua vector difficilis erit ad intelligendum in omni casu, et brevitas et ubertas recordationis permittit te videre multo maiorem partem programmatis in uno velo, quod tandem facilius intellegitur.
In hoc articulo programma in Q currum plenam efficiendum et experiri forsitan velis. Ad hoc faciendum, ipsa Q. egere potes liberam 32-bit versionem in kx societatis paginae - . Ibi, si interest, informationes in Q, libri invenies et varia de hoc argumento capitula.
DE PECCATO quaestio
Fons est qui mensam mittit cum singulis 25 milliseconds notitia. Cum KDB+ principaliter in rebus oeconomicis adhibetur, ponemus hanc esse tabulam negotiorum (artium), quae habet sequentes columnas: tempus (tempus milliseconds), sym (comitatio designatio in commercio stirpis - IBM, AAPL,. Intervallum XXV millisecondum arbitrarium est, nec nimium parvum nec longum. Praesentia eius significat notitias ad servitium iam buffered. Facile esset ad efficiendum buffering in parte muneris, inter dynamica buffering pendentia in onere currenti, sed pro simplicitate fixum intervallum ponemus.
Ministerium singulis momentis pro singulis advenientibus symbolo computare debet e sym columnae statuto functionum aggregationis - max pretis, avg pretii, summae magnitudinis, etc. utilis notitia. Pro simplicitate, ponemus omnes functiones incrementaliter computari posse, i.e. ut novum valorem obtineat, satis est duos numeros cognoscere, veteres et advenientes valores. Exempli gratia, munera max, mediocris, summa proprietatem hanc habent, sed munus mediana non habet.
Volumus etiam, ut rivus notitiae advenientis tempus iussum sit. Haec nobis facultas operandi solum ultimo minuto dabit. Re, satis est cum momentis currentibus et prioribus elaborare posse si quaedam recentia updates sunt. Pro simplici hoc casu non consideramus.
Aggregatio munera
Munera aggregationis debita infra recensentur. Earum quam plurimas accepi, ut onus in ministerium augerem;
- magno - max pretio - maximum pretium per minute.
- low - min price - minimum price per minute.
- firstPrice - first price - first price per minute.
- lastPrice - last price - last price per minute.
- firstSize - first size - first trade size per minute.
- lastSize - last size - last trade size in minutam.
- numTrades - count i - number of artium per minute.
- Volume - summa magnitudo - summa commercii magnitudinum per minutum.
- pvolume - summa pretia - summa pretia per minutias, pro avgPrice requisiti.
- - sum turnover price* size - total volumen transactionum per minute.
- avgPrice - pvolume%numTrades - mediocris pretium per minute.
- avgSize - volume%numTrades - mediocris artis magnitudo per minute.
- vwap - turnover% volume - mediocris pretium per minute praegravatis negotii magnitudine.
- cumVolume - summa volubilis - magnitudine rerum per totum tempus congesta.
De puncto non conspicuo statim discutiamus — quomodo has columnas primum initializes et minutas singulas subsequentes. Nonnullae primae quantitatis columnae singulis temporibus initiales fieri debent, earumque valor indefinitus est. Aliae figurae voluminis semper ad 0. Exstant etiam columnae quae accessum coniunctum requirunt - exempli gratia, cum Volumen ex priori minuto exscribendum sit, et primum ad 0. Ponamus omnes istos parametros utentes dictionarii notitia type (analogus to a record):
// list ! list – создать словарь, 0n – float null, 0N – long null, `sym – тип символ, `sym1`sym2 – список символов
initWith:`sym`time`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover`avgPrice`avgSize`vwap`cumVolume!(`;00:00;0n;0n;0n;0n;0N;0N;0;0;0.0;0.0;0n;0n;0n;0);
aggCols:reverse key[initWith] except `sym`time; // список всех вычисляемых колонок, reverse объяснен ниже
Addidi sym et tempus glossarium commoditatis, nunc initWith linea parata facta est e tabula finali aggregata, ubi superest ut rectam sym et tempus apponas. Ad mensam novos ordines addere potes.
Nos aggCols egebimus, cum munus aggregationis creando. Elenchus invertendus est ob ordinem quo aestimantur expressiones in Q (a dextra ad sinistram). Finis est curare calculus ab alto usque ad cumVolume, quoniam aliquae columnae a superioribus pendent.
Columnae quae ex priori novo minuto exscribendae sunt, ad commodam sym columnam additur:
rollColumns:`sym`cumVolume;
Nunc columnas dividamus in partes secundum quomodo renovandae sunt. Tria genera distingui possunt;
- Accumulatores (volumen, turnover,...) – accessum valorem priori adiciendum est.
- Peculiari puncto (alto, humili, ..) - prima valoris in minuto ex notitia advenientis accipitur, reliqua functione utentes computantur.
- Requiem. Semper ratione usus functionis.
Diffinimus variabiles hasce classes:
accumulatorCols:`numTrades`volume`pvolume`turnover;
specialCols:`high`low`firstPrice`firstSize;
Calculus ordo
Mensam aggregatam in duobus gradibus renovabimus. Ad efficientiam venientem tabulam primo abhorremus ita ut unus tantum ordo sit cuiusque characteris et momenti. Quod omnia munera nostra sunt cautiones incrementales et associativas quod effectus accessus additi gradus non mutatur. Mensam recusare poteras utens eligere:
select high:max price, low:min price … by sym,time.minute from table
Haec methodus incommodum habet - copia columnarum calculi praedefinita est. Fortunate, in Q, selectis etiam munus impletur ubi rationes dynamice creatae substituere potes;
?[table;whereClause;byClause;selectClause]
Formam argumentorum non singillatim describam, in casu tantum ac voces selectae nontriviales erunt et dictionaria columnarum formarum sint locutiones. Quapropter munus recusationis sic definiri potest:
selExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover!parse each ("max price";"min price";"first price";"last price";"first size";"last size";"count i";"sum size";"sum price";"sum price*size"); // each это функция map в Q для одного списка
preprocess:?[;();`sym`time!`sym`time.minute;selExpression];
Ad evidentiam, usus sum functione parse, quae filum cum expressione Q vertit in valorem qui ad munus coaevum transiri potest et quod in munere selecto requiritur. Notandum etiam quod preprocessus definitur proiectio (i.e., functio cum argumentis partim definitis) functionis selectae, unum argumentum deesse. Si praeprocessionem applicamus ad mensam, compressam mensam accipiemus.
Secundus gradus mensam aggregatam adaequat. Primum algorithmum in pseudocode scribamus:
for each sym in inputTable
idx: row index in agg table for sym+currentTime;
aggTable[idx;`high]: aggTable[idx;`high] | inputTable[sym;`high];
aggTable[idx;`volume]: aggTable[idx;`volume] + inputTable[sym;`volume];
…
In Q, commune est functionum tabularum reducere pro ansulis uti. Sed quia Q est lingua vector et omnes operationes ad omnia simul symbola facile possumus, deinde ad primam approximationem sine ansa facere possumus, operationes in omnibus symbolis simul peragentes;
idx:calcIdx inputTable;
row:aggTable idx;
aggTable[idx;`high]: row[`high] | inputTable`high;
aggTable[idx;`volume]: row[`volume] + inputTable`volume;
…
Sed ulterius progredi possumus, Q habet operator singularem et valde potentem - operator assignatio generalis. Permittit te mutare certa bona in structura notitia multiplici utens indice indices, munerum et argumentorum. In nostro casu sic spectat:
idx:calcIdx inputTable;
rows:aggTable idx;
// .[target;(idx0;idx1;..);function;argument] ~ target[idx 0;idx 1;…]: function[target[idx 0;idx 1;…];argument], в нашем случае функция – это присваивание
.[aggTable;(idx;aggCols);:;flip (row[`high] | inputTable`high;row[`volume] + inputTable`volume;…)];
Infeliciter, tabulae assignare debes index ordinum, non columnarum, et matricem (album columnarum ad indicem ordinum) transponere debes, functione flip. Haec carus est pro magna mensa, sic loco singulae columnae divisim assignationem generalem applicamus, functione geographica adhibita (quae sicut apostropha spectat);
.[aggTable;;:;]'[(idx;)each aggCols; (row[`high] | inputTable`high;row[`volume] + inputTable`volume;…)];
Munus proiectura iterum utimur. Etiam notandum est quod in Q, elenchum creans munus quoque esse et appellare possumus eum utentem singulas functiones ut indicem tabularum accipias.
Ut statuta columnarum calcularum non figatur, hanc expressionem alacriter efficiemus. Prius functiones definias ad singulas columnas computandas, variabilium versuum et inp utentium, referre ad notitias aggregatas et initus;
aggExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`avgPrice`avgSize`vwap`cumVolume!
("row[`high]|inp`high";"row[`low]&inp`low";"row`firstPrice";"inp`lastPrice";"row`firstSize";"inp`lastSize";"pvolume%numTrades";"volume%numTrades";"turnover%volume";"row[`cumVolume]+inp`volume");
Quaedam sunt speciales columnae, earum prima dignitas non pensari debet. Determinare possumus eam esse primam per columnam [`numTrades] — si contineat 0, primum valorem. Q munus selectum habet - ?[Index Boolean;list1;list2] - qui valorem eligit ex indice 1 vel 2 secundum conditionem primae argumenti:
// high -> ?[isFirst;inp`high;row[`high]|inp`high]
// @ - тоже обобщенное присваивание для случая когда индекс неглубокий
@[`aggExpression;specialCols;{[x;y]"?[isFirst;inp`",y,";",x,"]"};string specialCols];
Hic vocavi munus generale cum mea functione (expressio in crispo adstringit). Praesens valorem (primum argumentum) recipit et argumentum adiectum, quod in 4to parametro praetereo.
Seorsim adiciamus oratores altilium, quia idem est illis munus;
// volume -> row[`volume]+inp`volume
aggExpression[accumulatorCols]:{"row[`",x,"]+inp`",x } each string accumulatorCols;
Haec signa consueta est assignatio per Q, sed indicem valorum statim assigno. Postremo munus praecipuum crearemus;
// ":",/:aggExprs ~ map[{":",x};aggExpr] => ":row[`high]|inp`high" присвоим вычисленное значение переменной, потому что некоторые колонки зависят от уже вычисленных значений
// string[cols],'exprs ~ map[,;string[cols];exprs] => "high:row[`high]|inp`high" завершим создание присваивания. ,’ расшифровывается как map[concat]
// ";" sv exprs – String from Vector (sv), соединяет список строк вставляя “;” посредине
updateAgg:value "{[aggTable;idx;inp] row:aggTable idx; isFirst_0=row`numTrades; .[aggTable;;:;]'[(idx;)each aggCols;(",(";"sv string[aggCols],'":",/:aggExpression aggCols),")]}";
Hac expressione, dynamice munus creo ex filo quod supra dictum est continet. Eventus sic erit:
{[aggTable;idx;inp] rows:aggTable idx; isFirst_0=row`numTrades; .[aggTable;;:;]'[(idx;)each aggCols ;(cumVolume:row[`cumVolume]+inp`cumVolume;… ; high:?[isFirst;inp`high;row[`high]|inp`high])]}
Columna aestimatio ordinis invertitur eo quia ordo aestimationis in Q est a dextro ad sinistrum.
Nunc habemus duas praecipuas functiones ad calculos necessarias, tantum opus est ut paulo infrastructura addatur et ad servitium promptum.
Gradus finales
Praeprocessionem habemus et renovationes Agg functiones quae totum opus faciunt. Sed adhuc necessarium est ut rectam transitum per minutas et indices pro aggregatione curaret. Primum munus initum definiamus;
init:{
tradeAgg:: 0#enlist[initWith]; // создаем пустую типизированную таблицу, enlist превращает словарь в таблицу, а 0# означает взять 0 элементов из нее
currTime::00:00; // начнем с 0, :: означает, что присваивание в глобальную переменную
currSyms::`u#`symbol$(); // `u# - превращает список в дерево, для ускорения поиска элементов
offset::0; // индекс в tradeAgg, где начинается текущая минута
rollCache:: `sym xkey update `u#sym from rollColumns#tradeAgg; // кэш для последних значений roll колонок, таблица с ключом sym
}
Etiam munus volvi definiemus, quod currens minutum mutabit;
roll:{[tm]
if[currTime>tm; :init[]]; // если перевалили за полночь, то просто вызовем init
rollCache,::offset _ rollColumns#tradeAgg; // обновим кэш – взять roll колонки из aggTable, обрезать, вставить в rollCache
offset::count tradeAgg;
currSyms::`u#`$();
}
Munus nos oportet addere novas characteres:
addSyms:{[syms]
currSyms,::syms; // добавим в список известных
// добавим в таблицу sym, time и rollColumns воспользовавшись обобщенным присваиванием.
// Функция ^ подставляет значения по умолчанию для roll колонок, если символа нет в кэше. value flip table возвращает список колонок в таблице.
`tradeAgg upsert @[count[syms]#enlist initWith;`sym`time,cols rc;:;(syms;currTime), (initWith cols rc)^value flip rc:rollCache ([] sym: syms)];
}
Ac denique munus upd (nomen traditum huic functioni pro Q servitiis), quod a cliente dicitur, notitias addere;
upd:{[tblName;data] // tblName нам не нужно, но обычно сервис обрабатывает несколько таблиц
tm:exec distinct time from data:() xkey preprocess data; // preprocess & calc time
updMinute[data] each tm; // добавим данные для каждой минуты
};
updMinute:{[data;tm]
if[tm<>currTime; roll tm; currTime::tm]; // поменяем минуту, если необходимо
data:select from data where time=tm; // фильтрация
if[count msyms:syms where not (syms:data`sym)in currSyms; addSyms msyms]; // новые символы
updateAgg[`tradeAgg;offset+currSyms?syms;data]; // обновим агрегированную таблицу. Функция ? ищет индекс элементов списка справа в списке слева.
};
Id omne. Hic est integer codicis nostri officium, ut pollicitus est, paucas lineas;
initWith:`sym`time`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover`avgPrice`avgSize`vwap`cumVolume!(`;00:00;0n;0n;0n;0n;0N;0N;0;0;0.0;0.0;0n;0n;0n;0);
aggCols:reverse key[initWith] except `sym`time;
rollColumns:`sym`cumVolume;
accumulatorCols:`numTrades`volume`pvolume`turnover;
specialCols:`high`low`firstPrice`firstSize;
selExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`numTrades`volume`pvolume`turnover!parse each ("max price";"min price";"first price";"last price";"first size";"last size";"count i";"sum size";"sum price";"sum price*size");
preprocess:?[;();`sym`time!`sym`time.minute;selExpression];
aggExpression:`high`low`firstPrice`lastPrice`firstSize`lastSize`avgPrice`avgSize`vwap`cumVolume!("row[`high]|inp`high";"row[`low]&inp`low";"row`firstPrice";"inp`lastPrice";"row`firstSize";"inp`lastSize";"pvolume%numTrades";"volume%numTrades";"turnover%volume";"row[`cumVolume]+inp`volume");
@[`aggExpression;specialCols;{"?[isFirst;inp`",y,";",x,"]"};string specialCols];
aggExpression[accumulatorCols]:{"row[`",x,"]+inp`",x } each string accumulatorCols;
updateAgg:value "{[aggTable;idx;inp] row:aggTable idx; isFirst_0=row`numTrades; .[aggTable;;:;]'[(idx;)each aggCols;(",(";"sv string[aggCols],'":",/:aggExpression aggCols),")]}"; / '
init:{
tradeAgg::0#enlist[initWith];
currTime::00:00;
currSyms::`u#`symbol$();
offset::0;
rollCache:: `sym xkey update `u#sym from rollColumns#tradeAgg;
};
roll:{[tm]
if[currTime>tm; :init[]];
rollCache,::offset _ rollColumns#tradeAgg;
offset::count tradeAgg;
currSyms::`u#`$();
};
addSyms:{[syms]
currSyms,::syms;
`tradeAgg upsert @[count[syms]#enlist initWith;`sym`time,cols rc;:;(syms;currTime),(initWith cols rc)^value flip rc:rollCache ([] sym: syms)];
};
upd:{[tblName;data] updMinute[data] each exec distinct time from data:() xkey preprocess data};
updMinute:{[data;tm]
if[tm<>currTime; roll tm; currTime::tm];
data:select from data where time=tm;
if[count msyms:syms where not (syms:data`sym)in currSyms; addSyms msyms];
updateAgg[`tradeAgg;offset+currSyms?syms;data];
};
temptationis
Inhibeamus observantiam ministerii. Quod ut facias, illud in processu separato (pone signum in servicio.q fasciculi) curramus et munus initum vocamus:
q service.q –p 5566
q)init[]
In alio consolare, incipit in secundo processu Q et coniunge primo;
h:hopen `:host:5566
h:hopen 5566 // если оба на одном хосте
Primum indicem faciamus symbolorum - 10000 frusta et munus addere ad mensam temere creandam. In secundo console;
syms:`IBM`AAPL`GOOG,-9997?`8
rnd:{[n;t] ([] sym:n?syms; time:t+asc n#til 25; price:n?10f; size:n?10)}
Tria symbola realia addidi in albo, quo facilius ea in tabula quaererem. Munus rnd mensam temere cum n ordinibus creat, ubi tempus ab t ad t+25 milliseconds variatur.
Nunc experiri potes ut notitias mittens ad officium (primas decem horas adde);
{h (`upd;`trade;rnd[10000;x])} each `time$00:00 + til 60*10
Potes inspicere in servitio quod mensa renovata est:
c 25 200
select from tradeAgg where sym=`AAPL
-20#select from tradeAgg where sym=`AAPL
effectus:
sym|time|high|low|firstPrice|lastPrice|firstSize|lastSize|numTrades|volume|pvolume|turnover|avgPrice|avgSize|vwap|cumVolume
--|--|--|--|--|--------------------------------
AAPL|09:27|9.258904|9.258904|9.258904|9.258904|8|8|1|8|9.258904|74.07123|9.258904|8|9.258904|2888
AAPL|09:28|9.068162|9.068162|9.068162|9.068162|7|7|1|7|9.068162|63.47713|9.068162|7|9.068162|2895
AAPL|09:31|4.680449|0.2011121|1.620827|0.2011121|1|5|4|14|9.569556|36.84342|2.392389|3.5|2.631673|2909
AAPL|09:33|2.812535|2.812535|2.812535|2.812535|6|6|1|6|2.812535|16.87521|2.812535|6|2.812535|2915
AAPL|09:34|5.099025|5.099025|5.099025|5.099025|4|4|1|4|5.099025|20.3961|5.099025|4|5.099025|2919Nunc onere experiamur ut sciamus quanti notitia muneris per minutias processum facere possit. Fac me monere te intervallum renovationis 25 milliseconds constituisse. Proinde officium (mediocris) aptum est saltem viginti millium secundorum per renovationem ad utentes tempus dare petendi notitias. Intra sequentia in secundo processu:
tm:10:00:00.000
stressTest:{[n] 1 string[tm]," "; times,::h ({st:.z.T; upd[`trade;x]; .z.T-st};rnd[n;tm]); tm+:25}
start:{[n] times::(); do[4800;stressTest[n]]; -1 " "; `min`avg`med`max!(min times;avg times;med times;max times)}
(IV)DCCC duo minuta est. Tentare potes primum pro 4800 ordinibus singulis 1000 milliseconds currentem:
start 1000
In casu meo, effectus est circiter duorum millium secundorum per update. Itaque statim numerum versuum ad 10.000 augebo;
start 10000
effectus:
min| 00:00:00.004
avg| 9.191458
med| 9f
max| 00:00:00.030
Iterum nihil speciale, sed hoc est 24 decies centena millia linearum per minutias, 400 mille per secundam. Plus quam 25 millium secundorum millium secundorum renovatio retardatur tantum 5 vicibus, ut videtur, cum minutum mutatur. Crescamus ad 100.000:
start 100000
effectus:
min| 00:00:00.013
avg| 25.11083
med| 24f
max| 00:00:00.108
q)sum times
00:02:00.532
Ut videre potes, ministerium vix tolerare potes, sed tamen administrat meatum manere. Tale volumen notitiarum (CCXL milium ordinum per minutum) perquam magnum est, in quo casu commune est ut complures clones (vel etiam justos clones) servitii emittant, quorum unumquemque tantum partem characterum procedit. Tamen, eventus gravis est ad linguam interpretandam quae principaliter in notitia repono.
Quaeri potest, cur tempus non-lineaaliter cum magnitudine uniuscuiusque renovationis crescat. Ratio est, quia recusatum munus est actu functionis C, quae multo efficacior est quam renovatio Agg. Incipiens a quadam magnitudine renovationis (circa 10.000), renovatio Agg lacunam suam attingit ac deinde tempus exsecutionis eius in magnitudine renovationis non pendet. Ob praelibatum gradum Q ut religio possit tales notitias volumina concoquere. Hoc elucidat quanti momenti sit eligere ius algorithmum cum operando cum magnis data. Aliud punctum est recta tabularia notitiarum in memoria. Si notitia electronica non reponeretur vel tempore non ordinata essemus, tunc nota fierimus cum tali re quae deesset TLB cache - absentia memoriae paginae electronicae in processu electronici cella. Investigatio inscriptionis circiter XXX vicibus accipit si parum prospere, et si notitiae dissipatae sunt, servitium pluries retardare potest.
conclusio,
In hoc articulo demonstravi datorum KDB+ et Q apta esse non solum ad magnas notitias accommodandas et facile per selectas accessiones, sed etiam ad operas processus notitias conficiendas, quae centenis decies centena milium versuum/gigabytorum notitiarum concoquendarum capaces sunt. Q processus . Lingua ipsa Q permittit ad exsecutionem algorithmorum valde concisam et efficacem pertinentium ad processus notitiae ex natura sua vectoris, in dialecto SQL interpres constructa et functiones bibliothecae felicissimae.
Notabo hanc partem iustam esse quid Q facere possit, et alias singulares notas habere. Exempli gratia, protocollum IPC simplicissimum, quod limitem inter singulos Q processuum delet et centum horum processuum in unum retis coniungere sinit, quae in justo servientium in diversis mundi partibus collocari potest.
Source: www.habr.com
