Accelerated Q Tips
Why Q Tips?
- Unique perspective
- Biggest impact
- Show. Don’t tell
What is Q Tips?
- Learn by example
- Q-SQL starts with q
- Q is fun
$ $QHOME/$QARCH/q cep.q -help
KDB+ 3.2 2015.03.04 Copyright (C) 1993-2015 Kx Systems
m32/ 4()core 4096MB nick nick-macbook-pro.local 172.30.0.169 NONEXPIRE  
usage: q cep.q [option]...
       -ref   <file with reference data>  (`:ref.csv)           
       -eod   <time for end of day event> (0D23:59:00.000000000)
       -db    <end of day dump location>  (`:db)                
       -debug <don't start engine>        (0b)                  
       -log   <log level>                 (2)                   
Normal Random Variables
- 
    Uniforms q).hist.fdhist 100?1f 0.001110398| 21 "********************* " 0.1991757 | 21 "********************* " 0.397241 | 21 "********************* " 0.5953062 | 17 "***************** " 0.7933715 | 19 "******************* " 0.9914368 | 1 "* "
- 
    12 Uniforms u12:{-6f+sum x cut (12*x)?1f}q).hist.fdhist .stat.u12 100 -2.633577 | 1 "* " -2.141205 | 3 "*** " -1.648833 | 8 "******** " -1.156461 | 6 "****** " -0.664089 | 20 "******************** " -0.1717169| 12 "************ " 0.3206552 | 29 "***************************** " 0.8130273 | 8 "******** " 1.305399 | 10 "********** " 1.797772 | 2 "** " 2.290144 | 1 "* "
- 
    Box-Muller bm:{ if[count[x] mod 2;'`length]; x:2 0N#x; r:sqrt -2f*log x 0; theta:2f*acos[-1]*x 1; x: r*cos theta; x,:r*sin theta; x}
- Use ifstatements to exit functions early
- Use compound assignment for increased efficiency
- Use the closing brace to document a function’s return value
Simulated Security Paths
- 
    Geometric Brownian Motion gbm:{[s;r;t;z]exp(t*r-.5*s*s)+z*s*sqrt t}
- 
    Chain functions q)s:.3;r:.05;dt:.util.wday 2001.01.01+til 365 q)tm:deltas[first dt;dt]%365.25 q)100*prds .stat.gbm[s;r;tm] .stat.norminv (count dt)?1f 100 100.1525 98.91791 97.59687 96.99619 94.84253 93.80587 92.75921 91.29922..
- Use the dyadic form of deltas,ratios,differandprev
- Factor algorithms into small reusable pieces
- Reserve the last function parameter for data
Simulations
- 
    Tables q).sim.genp[0;100;.3;.03] .util.rng[1;2000.01.01;2001.01.01] id date price ---------------------- 0 2000.01.01 100 0 2000.01.02 98.69405 0 2000.01.03 98.27505 0 2000.01.04 98.52507 0 2000.01.05 99.09263 ..q).sim.genp[0;100;.3;.03] .util.rng[1;09:00;16:00] id time price ----------------- 0 09:00 100 0 09:01 100.0323 0 09:02 99.98794 0 09:03 99.95417 0 09:04 99.99175 ..
- 
    Eaching q)n:10 q)dts:.util.rng[1;2000.01.01;2001.01.01] q)2!raze sim.genp[;;;;dts]'[til n;n?100;n?.3;n?.03] id date | price -------------| -------- 0 2000.01.01| 89 0 2000.01.02| 88.66867 0 2000.01.03| 88.52082 0 2000.01.04| 87.29726 0 2000.01.05| 85.9732 ..
- 
    Return unkeyed tables by default 
Attributes
- 
    Sorted q)select avg px by id from prices id| px --| -------- 0 | 109.9929 1 | 80.01755 2 | 10.00047 3 | 5.000112 4 | 1.999113q)meta select avg px by id from prices c | t f a --| ----- id| j s px| f
- 
    Unique q)n:10000 q)d1:(k:til n)!v:n?1f q)show d2:(`u#k)!v 0| 0.3425698 1| 0.4950533 2| 0.360046 3| 0.2700259 ..q)\t:10000 d1 5000 90 q)\t:10000 d2 5000 7
- Use the `uattribute on dictionary keys to increase performance
- 
    Use \t:nand\ts:nto time multiple runs of a single command
- 
    Partition q) select avg px by id,time.minute from prices id minute| px ---------| -------- 0 22:54 | 110.0092 0 22:55 | 110.0051 0 22:56 | 109.9962 1 22:54 | 80.00557 1 22:55 | 80.02086 ..q)meta select avg px by id,time.minute from prices c | t f a ------| ----- id | j p minute| u px | f
- 
    Group q)show trade:flip`id`time`price`size!"jpfi"$\:() id time price size ------------------sattr:{[t] c:first cols t; a:`g`u 1=n:count keys t; t:n!@[;c;a#]0!t; t}q)meta .util.sattr trade c | t f a -----| ----- id | j g time | p price| f size | i
Compression
- 
    Tree tree:{$[x~k:key x;x;11h=type k;raze (.z.s ` sv x,) each k;()]}q).util.tree `:qdb `:qdb/2015.03.03/price/.d`:qdb/2015.03.03/price/id`:qdb/2015.03.03/price/px.. q).util.tree ` `.q.`.q.neg`.q.not`.q.null`.q.string`.q.reciprocal`.q.floor`.q.ceiling`.q.s..
- 
    Compress q).z.zd:20 2 9 q){x set get x} each .util.tree `:qdb `:qdb/2015.03.06/prices/.d`:qdb/2015.03.06/prices/id`:qdb/2015.03.06/prices..
- 
    Use .z.sto make recursive function calls
q-SQL
- 
    Verbose use of selectq)s:select id,date.week,p:price from t q)select o:first p,h:max p,l:min p,c:last p by id,week from s id week | o h l c -------------| ----------------------------------- 0 1999.12.27| 100 100.0632 100 100.0632 0 2000.01.03| 100.122 100.122 97.507 99.71328 0 2000.01.10| 99.36008 99.63464 95.70985 96.14122 0 2000.01.17| 96.24896 99.87957 95.87985 99.87957 0 2000.01.24| 98.72707 103.3749 98.72707 102.9145 ..
- 
    Elegant use of execohlc:{`o`h`l`c!(first;max;min;last)@\:x}q)exec .stat.ohlc price by id,date.week from t id week | o h l c -------------| ----------------------------------- 0 1999.12.27| 100 100.0632 100 100.0632 0 2000.01.03| 100.122 100.122 97.507 99.71328 0 2000.01.10| 99.36008 99.63464 95.70985 96.14122 0 2000.01.17| 96.24896 99.87957 95.87985 99.87957 0 2000.01.24| 98.72707 103.3749 98.72707 102.9145 ..
- Factor complex q-SQL statements into functions
- Simplify queries by using execby
Pivot
- 
    Simple interface pivot:{[t] u:`$string asc distinct last f:flip key t; pf:{x#(`$string y)!z}; p:?[t;();g!g:-1_ k;(pf;`u;last k:key f;last key flip value t)]; p}
- 
    Flexible use q)"i"$.util.pivot select by id,date.year from t id| 2000 2001 2002 2003 2004 --| ------------------------ 0 | 95 130 233 224 237 1 | 138 165 229 205 239 2 | 121 121 88 105 84 3 | 97 62 117 146 237 4 | 67 84 93 147 156 ..
- 
    Use functional select+/+updateto parameterizebyclauses
- 
    Use it everywhere q)"i"$.util.pivot select by date.year,id from t year| 0 1 2 3 4 5 6 7 8 9 ----| ------------------------------------ 2000| 95 138 121 97 67 70 57 87 102 100 2001| 130 165 121 62 84 64 44 72 161 103 2002| 233 229 88 117 93 65 32 53 159 112 2003| 224 205 105 146 147 57 26 56 291 130 2004| 237 239 84 237 156 63 30 55 256 85
- 
    Only transform tables into pivot tables for presentation 
Grid Computing
- 
    Multiple Slaves $ q qdb -p 5000 -s -4 KDB+ 3.2 2015.03.04 Copyright (C) 1993-2015 Kx Systems m32/ 4()core 2048MB nick nicks-macbook.local 192.168.1.103 NONEXPIRE q)(system "q . -p ",) each string p:system["p"]+1+til neg system"s" q).z.pd:`u#hopen each p
- 
    Multi-process peachq)select pid:.z.i by date from trades date | pid ----------| ----- 2015.03.03| 52436 2015.03.04| 52438 2015.03.05| 52440 2015.03.06| 52436
Profiling
- 
    Timing time:{[n;f;a] s:.z.p; id:.prof.id+:1; pid:.prof.pid; .prof.pid:id; r:f . a; .prof.pid:pid; `prof.events upsert (id;pid;n;.z.p-s); r}
- 
    Instrumenting instr:{[n] m:get f:get n; system "d .",string first m 3; n set (')[.prof.time[n;f];enlist]; system "d ."; n}
- 
    Recording q)prof.events id pid func time ---------------------------------------- 4 3 .sim.tickrnd 0D00:00:00.000010000 3 2 .md.updq 0D00:00:00.000235000 2 1 .timer.until 0D00:00:00.000276000 1 0 .timer.run 0D00:00:00.000442000 8 7 .sim.tickrnd 0D00:00:00.000007000 ..
- 
    Reporting q)prof.rpt func | time n nc timepc pct ------------| ------------------------------------- .timer.merge| 1590.278 25553 0 0.06223449 19.18796 .timer.run | 1378.854 25553 2 0.05396055 16.63696 .md.updp | 1246.257 16400 2 0.07599128 15.03708 .md.updq | 841.354 6178 1 0.1361855 10.1516 .stat.horner| 690.185 49200 0 0.01402815 8.327628 ..
Derivative Pricing
- 
    Monte Carlo mc:{[S;s;r;t;pf;n] z:.stat.norminv n?/:count[t]#1f; f:S*.stat.gbm[s;r;deltas[first t;t]] z; v:pf[f]*exp neg r*last t; v}
- 
    Payoffs eu:{[c;k;f]0f|$[c;last[f]-k;k-last f]}q).util.use `.deriv; q)c:1b;S:100;k:90;s:.2;r:.03 q)t:til[252]%251;n:10000 q)mcstat raze mc[S;s;r;t;eu[c;k]] peach 20#n ev | 15.41932 err| 0.0741624 n | 200000
- Use scalar conditional $[;;]to implement lazily evaluated blocks
- 
    Use peachto run computations in parallel
- 
    Up and Out Barrier Option bo:{[bf;pf;f]bf[f]*pf f}q)c:1b;S:100;k:90;s:.2;r:.03 q)mcstat raze mc[S;s;r;t;bo[all 120>]eu[c;k]] peach 20#n ev | 3.990454 err| 0.02974377 n | 200000
- Use mathematical operations instead of conditionals
Histograms
- 
    Reusable Components q).util.use `.hist q)chart[bar"-";30] count each bgroup[sqrtn] .stat.bm 100?1f -2.498147 | 1 "- " -1.974753 | 8 "-------- " -1.451358 | 8 "-------- " -0.9279628| 14 "-------------- " -0.4045679| 16 "---------------- " 0.118827 | 22 "---------------------- " 0.6422219 | 16 "---------------- " 1.165617 | 10 "---------- " 1.689012 | 3 "--- " 2.212407 | 2 "-- " 2.735802 | 0 " "
- 
    Encourages Reuse q)chart[dot"*";30] count each bgroup[doane] .stat.bm 100?1f -2.45497 | 2 " * " -1.871575 | 5 " * " -1.28818 | 14 " * " -0.7047846| 22 " * " -0.1213896| 25 " * " 0.4620055 | 14 " * " 1.045401 | 9 " * " 1.628796 | 7 " * " 2.212191 | 2 " * " 2.795586 | 0 " "
- 
    Pursue the functional-vector solution 
