KDB+ ಬೇಸ್, Q ಪ್ರೋಗ್ರಾಮಿಂಗ್ ಭಾಷೆ ಏನು, ನನ್ನ ಹಿಂದಿನ ಸಾಮರ್ಥ್ಯ ಮತ್ತು ದೌರ್ಬಲ್ಯಗಳ ಬಗ್ಗೆ ನೀವು ಓದಬಹುದು ಮತ್ತು ಸಂಕ್ಷಿಪ್ತವಾಗಿ ಪರಿಚಯದಲ್ಲಿ. ಲೇಖನದಲ್ಲಿ, ಒಳಬರುವ ಡೇಟಾ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುವಂತಹ ಸೇವೆಯನ್ನು ನಾವು Q ನಲ್ಲಿ ಕಾರ್ಯಗತಗೊಳಿಸುತ್ತೇವೆ ಮತ್ತು "ನೈಜ ಸಮಯ" ಮೋಡ್ನಲ್ಲಿ ಪ್ರತಿ ನಿಮಿಷಕ್ಕೆ ವಿವಿಧ ಒಟ್ಟುಗೂಡಿಸುವ ಕಾರ್ಯಗಳನ್ನು ಲೆಕ್ಕಾಚಾರ ಮಾಡುತ್ತೇವೆ (ಅಂದರೆ, ಡೇಟಾದ ಮುಂದಿನ ಭಾಗದ ಮೊದಲು ಎಲ್ಲವನ್ನೂ ಲೆಕ್ಕಾಚಾರ ಮಾಡಲು ಸಮಯವಿರುತ್ತದೆ). Q ಯ ಮುಖ್ಯ ಲಕ್ಷಣವೆಂದರೆ ಇದು ವೆಕ್ಟರ್ ಭಾಷೆಯಾಗಿದ್ದು ಅದು ಒಂದೇ ವಸ್ತುಗಳೊಂದಿಗೆ ಅಲ್ಲ, ಆದರೆ ಅವುಗಳ ಸರಣಿಗಳು, ಸರಣಿಗಳ ಸರಣಿಗಳು ಮತ್ತು ಇತರ ಸಂಕೀರ್ಣ ವಸ್ತುಗಳೊಂದಿಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸಲು ನಿಮಗೆ ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ. Q ಮತ್ತು ಅದರ ಸಂಬಂಧಿಗಳಾದ K, J, APL ನಂತಹ ಭಾಷೆಗಳು ಅವುಗಳ ಸಂಕ್ಷಿಪ್ತತೆಗೆ ಪ್ರಸಿದ್ಧವಾಗಿವೆ. ಸಾಮಾನ್ಯವಾಗಿ, ಜಾವಾದಂತಹ ಪರಿಚಿತ ಭಾಷೆಯಲ್ಲಿ ಕೋಡ್ನ ಹಲವಾರು ಪರದೆಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುವ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕೆಲವು ಸಾಲುಗಳಲ್ಲಿ ಬರೆಯಬಹುದು. ಈ ಲೇಖನದಲ್ಲಿ ನಾನು ಪ್ರದರ್ಶಿಸಲು ಬಯಸುತ್ತೇನೆ.

ಪರಿಚಯ
KDB+ ಎಂಬುದು ಸ್ತಂಭಾಕಾರದ ಡೇಟಾಬೇಸ್ ಆಗಿದ್ದು, ಹೆಚ್ಚಿನ ಪ್ರಮಾಣದ ಡೇಟಾದ ಮೇಲೆ ಕೇಂದ್ರೀಕೃತವಾಗಿದೆ, ನಿರ್ದಿಷ್ಟ ರೀತಿಯಲ್ಲಿ (ಪ್ರಾಥಮಿಕವಾಗಿ ಸಮಯದ ಪ್ರಕಾರ) ಆದೇಶಿಸಲಾಗಿದೆ. ಇದನ್ನು ಪ್ರಾಥಮಿಕವಾಗಿ ಹಣಕಾಸು ಸಂಸ್ಥೆಗಳಲ್ಲಿ ಬಳಸಲಾಗುತ್ತದೆ - ಬ್ಯಾಂಕುಗಳು, ಹೂಡಿಕೆ ನಿಧಿಗಳು, ವಿಮಾ ಕಂಪನಿಗಳು. Q ಭಾಷೆಯು KDB+ ನ ಆಂತರಿಕ ಭಾಷೆಯಾಗಿದ್ದು ಅದು ಈ ಡೇಟಾದೊಂದಿಗೆ ಪರಿಣಾಮಕಾರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ. Q ಸಿದ್ಧಾಂತವು ಸಂಕ್ಷಿಪ್ತತೆ ಮತ್ತು ದಕ್ಷತೆಯಾಗಿದೆ, ಆದರೆ ಸ್ಪಷ್ಟತೆಯನ್ನು ತ್ಯಾಗ ಮಾಡಲಾಗುತ್ತದೆ. ಯಾವುದೇ ಸಂದರ್ಭದಲ್ಲಿ ವೆಕ್ಟರ್ ಭಾಷೆಯನ್ನು ಅರ್ಥಮಾಡಿಕೊಳ್ಳಲು ಕಷ್ಟವಾಗುತ್ತದೆ ಎಂಬ ಅಂಶದಿಂದ ಇದನ್ನು ಸಮರ್ಥಿಸಲಾಗುತ್ತದೆ, ಮತ್ತು ರೆಕಾರ್ಡಿಂಗ್ನ ಸಂಕ್ಷಿಪ್ತತೆ ಮತ್ತು ಶ್ರೀಮಂತಿಕೆಯು ಪ್ರೋಗ್ರಾಂನ ಹೆಚ್ಚಿನ ಭಾಗವನ್ನು ಒಂದೇ ಪರದೆಯಲ್ಲಿ ನೋಡಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಇದು ಅಂತಿಮವಾಗಿ ಅರ್ಥಮಾಡಿಕೊಳ್ಳಲು ಸುಲಭವಾಗುತ್ತದೆ.
ಈ ಲೇಖನದಲ್ಲಿ ನಾವು Q ನಲ್ಲಿ ಪೂರ್ಣ ಪ್ರಮಾಣದ ಪ್ರೋಗ್ರಾಂ ಅನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸುತ್ತೇವೆ ಮತ್ತು ನೀವು ಅದನ್ನು ಪ್ರಯತ್ನಿಸಲು ಬಯಸಬಹುದು. ಇದನ್ನು ಮಾಡಲು, ನಿಮಗೆ ನಿಜವಾದ Q ಅಗತ್ಯವಿರುತ್ತದೆ. ನೀವು kx ಕಂಪನಿಯ ವೆಬ್ಸೈಟ್ನಲ್ಲಿ ಉಚಿತ 32-ಬಿಟ್ ಆವೃತ್ತಿಯನ್ನು ಡೌನ್ಲೋಡ್ ಮಾಡಬಹುದು - . ಅಲ್ಲಿ, ನಿಮಗೆ ಆಸಕ್ತಿ ಇದ್ದರೆ, ಪ್ರಶ್ನೆ, ಪುಸ್ತಕದ ಉಲ್ಲೇಖದ ಮಾಹಿತಿಯನ್ನು ನೀವು ಕಾಣಬಹುದು ಮತ್ತು ಈ ವಿಷಯದ ಬಗ್ಗೆ ವಿವಿಧ ಲೇಖನಗಳು.
ಸಮಸ್ಯೆ ಹೇಳಿಕೆ
ಪ್ರತಿ 25 ಮಿಲಿಸೆಕೆಂಡ್ಗಳಿಗೆ ಡೇಟಾದೊಂದಿಗೆ ಟೇಬಲ್ ಕಳುಹಿಸುವ ಮೂಲವಿದೆ. KDB+ ಅನ್ನು ಪ್ರಾಥಮಿಕವಾಗಿ ಹಣಕಾಸಿನಲ್ಲಿ ಬಳಸಲಾಗಿರುವುದರಿಂದ, ಇದು ವಹಿವಾಟುಗಳ (ಟ್ರೇಡ್ಗಳು) ಟೇಬಲ್ ಎಂದು ನಾವು ಭಾವಿಸುತ್ತೇವೆ, ಇದು ಈ ಕೆಳಗಿನ ಕಾಲಮ್ಗಳನ್ನು ಹೊಂದಿದೆ: ಸಮಯ (ಮಿಲಿಸೆಕೆಂಡ್ಗಳಲ್ಲಿ ಸಮಯ), ಸಿಮ್ (ಸ್ಟಾಕ್ ಎಕ್ಸ್ಚೇಂಜ್ನಲ್ಲಿ ಕಂಪನಿಯ ಪದನಾಮ - ಐಬಿಎಂ, AAPL,,...), ಬೆಲೆ (ಷೇರುಗಳನ್ನು ಖರೀದಿಸಿದ ಬೆಲೆ), ಗಾತ್ರ (ವ್ಯವಹಾರದ ಗಾತ್ರ). 25 ಮಿಲಿಸೆಕೆಂಡ್ ಮಧ್ಯಂತರವು ಅನಿಯಂತ್ರಿತವಾಗಿದೆ, ತುಂಬಾ ಚಿಕ್ಕದಲ್ಲ ಮತ್ತು ತುಂಬಾ ಉದ್ದವಾಗಿಲ್ಲ. ಅದರ ಉಪಸ್ಥಿತಿಯು ಡೇಟಾವು ಈಗಾಗಲೇ ಬಫರ್ ಮಾಡಿದ ಸೇವೆಗೆ ಬರುತ್ತದೆ ಎಂದರ್ಥ. ಪ್ರಸ್ತುತ ಲೋಡ್ ಅನ್ನು ಅವಲಂಬಿಸಿ ಡೈನಾಮಿಕ್ ಬಫರಿಂಗ್ ಸೇರಿದಂತೆ ಸೇವೆಯ ಬದಿಯಲ್ಲಿ ಬಫರಿಂಗ್ ಅನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸುವುದು ಸುಲಭ, ಆದರೆ ಸರಳತೆಗಾಗಿ, ನಾವು ಸ್ಥಿರ ಮಧ್ಯಂತರವನ್ನು ಕೇಂದ್ರೀಕರಿಸುತ್ತೇವೆ.
ಸೇವೆಯು ಸಿಮ್ ಕಾಲಮ್ನಿಂದ ಪ್ರತಿ ಒಳಬರುವ ಚಿಹ್ನೆಗೆ ಪ್ರತಿ ನಿಮಿಷವನ್ನು ಎಣಿಸಬೇಕು - ಒಟ್ಟುಗೂಡಿಸುವ ಕಾರ್ಯಗಳ ಒಂದು ಸೆಟ್ - ಗರಿಷ್ಠ ಬೆಲೆ, ಸರಾಸರಿ ಬೆಲೆ, ಮೊತ್ತದ ಗಾತ್ರ, ಇತ್ಯಾದಿ. ಉಪಯುಕ್ತ ಮಾಹಿತಿ. ಸರಳತೆಗಾಗಿ, ಎಲ್ಲಾ ಕಾರ್ಯಗಳನ್ನು ಕ್ರಮೇಣವಾಗಿ ಲೆಕ್ಕಹಾಕಬಹುದು ಎಂದು ನಾವು ಭಾವಿಸುತ್ತೇವೆ, ಅಂದರೆ. ಹೊಸ ಮೌಲ್ಯವನ್ನು ಪಡೆಯಲು, ಎರಡು ಸಂಖ್ಯೆಗಳನ್ನು ತಿಳಿದುಕೊಳ್ಳುವುದು ಸಾಕು - ಹಳೆಯ ಮತ್ತು ಒಳಬರುವ ಮೌಲ್ಯಗಳು. ಉದಾಹರಣೆಗೆ, ಗರಿಷ್ಟ, ಸರಾಸರಿ, ಮೊತ್ತದ ಕಾರ್ಯಗಳು ಈ ಗುಣವನ್ನು ಹೊಂದಿವೆ, ಆದರೆ ಸರಾಸರಿ ಕಾರ್ಯವು ಹೊಂದಿಲ್ಲ.
ಒಳಬರುವ ಡೇಟಾ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಸಮಯಕ್ಕೆ ಆದೇಶಿಸಲಾಗಿದೆ ಎಂದು ನಾವು ಭಾವಿಸುತ್ತೇವೆ. ಇದು ನಮಗೆ ಕೊನೆಯ ಕ್ಷಣದಲ್ಲಿ ಮಾತ್ರ ಕೆಲಸ ಮಾಡುವ ಅವಕಾಶವನ್ನು ನೀಡುತ್ತದೆ. ಪ್ರಾಯೋಗಿಕವಾಗಿ, ಕೆಲವು ನವೀಕರಣಗಳು ತಡವಾಗಿದ್ದರೆ ಪ್ರಸ್ತುತ ಮತ್ತು ಹಿಂದಿನ ನಿಮಿಷಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ. ಸರಳತೆಗಾಗಿ, ನಾವು ಈ ಪ್ರಕರಣವನ್ನು ಪರಿಗಣಿಸುವುದಿಲ್ಲ.
ಒಟ್ಟುಗೂಡಿಸುವ ಕಾರ್ಯಗಳು
ಅಗತ್ಯವಿರುವ ಒಟ್ಟುಗೂಡಿಸುವ ಕಾರ್ಯಗಳನ್ನು ಕೆಳಗೆ ಪಟ್ಟಿ ಮಾಡಲಾಗಿದೆ. ಸೇವೆಯ ಮೇಲಿನ ಹೊರೆ ಹೆಚ್ಚಿಸಲು ನಾನು ಅವುಗಳಲ್ಲಿ ಹೆಚ್ಚಿನದನ್ನು ತೆಗೆದುಕೊಂಡಿದ್ದೇನೆ:
- ಹೆಚ್ಚಿನ - ಗರಿಷ್ಠ ಬೆಲೆ - ನಿಮಿಷಕ್ಕೆ ಗರಿಷ್ಠ ಬೆಲೆ.
- ಕಡಿಮೆ - ನಿಮಿಷ ಬೆಲೆ - ಪ್ರತಿ ನಿಮಿಷಕ್ಕೆ ಕನಿಷ್ಠ ಬೆಲೆ.
- ಮೊದಲ ಬೆಲೆ - ಮೊದಲ ಬೆಲೆ - ನಿಮಿಷಕ್ಕೆ ಮೊದಲ ಬೆಲೆ.
- ಕೊನೆಯ ಬೆಲೆ - ಕೊನೆಯ ಬೆಲೆ - ನಿಮಿಷಕ್ಕೆ ಕೊನೆಯ ಬೆಲೆ.
- ಮೊದಲ ಗಾತ್ರ - ಮೊದಲ ಗಾತ್ರ - ನಿಮಿಷಕ್ಕೆ ಮೊದಲ ವ್ಯಾಪಾರ ಗಾತ್ರ.
- lastSize - ಕೊನೆಯ ಗಾತ್ರ - ಒಂದು ನಿಮಿಷದಲ್ಲಿ ಕೊನೆಯ ವ್ಯಾಪಾರದ ಗಾತ್ರ.
- numTrades - ಎಣಿಕೆ i - ನಿಮಿಷಕ್ಕೆ ವಹಿವಾಟುಗಳ ಸಂಖ್ಯೆ.
- ಪರಿಮಾಣ - ಮೊತ್ತದ ಗಾತ್ರ - ಪ್ರತಿ ನಿಮಿಷಕ್ಕೆ ವ್ಯಾಪಾರ ಗಾತ್ರಗಳ ಮೊತ್ತ.
- pvolume - ಮೊತ್ತದ ಬೆಲೆ - ಪ್ರತಿ ನಿಮಿಷಕ್ಕೆ ಬೆಲೆಗಳ ಮೊತ್ತ, ಸರಾಸರಿ ಬೆಲೆಗೆ ಅಗತ್ಯವಿದೆ.
- - ಮೊತ್ತದ ವಹಿವಾಟು ಬೆಲೆ * ಗಾತ್ರ - ಪ್ರತಿ ನಿಮಿಷಕ್ಕೆ ವಹಿವಾಟುಗಳ ಒಟ್ಟು ಪ್ರಮಾಣ.
- avgPrice - pvolume%numTrades - ಪ್ರತಿ ನಿಮಿಷಕ್ಕೆ ಸರಾಸರಿ ಬೆಲೆ.
- avgSize – volume%numTrades – ಪ್ರತಿ ನಿಮಿಷಕ್ಕೆ ಸರಾಸರಿ ವ್ಯಾಪಾರದ ಗಾತ್ರ.
- vwap - ವಹಿವಾಟು% ಪರಿಮಾಣ - ವಹಿವಾಟಿನ ಗಾತ್ರದಿಂದ ಪ್ರತಿ ನಿಮಿಷಕ್ಕೆ ಸರಾಸರಿ ಬೆಲೆ.
- cumVolume - ಮೊತ್ತದ ಪರಿಮಾಣ - ಸಂಪೂರ್ಣ ಸಮಯದ ವಹಿವಾಟಿನ ಸಂಚಿತ ಗಾತ್ರ.
ಒಂದು ಸ್ಪಷ್ಟವಲ್ಲದ ಅಂಶವನ್ನು ತಕ್ಷಣವೇ ಚರ್ಚಿಸೋಣ - ಈ ಕಾಲಮ್ಗಳನ್ನು ಮೊದಲ ಬಾರಿಗೆ ಮತ್ತು ಪ್ರತಿ ನಂತರದ ನಿಮಿಷಕ್ಕೆ ಹೇಗೆ ಪ್ರಾರಂಭಿಸುವುದು. ಮೊದಲ ಬೆಲೆಯ ಪ್ರಕಾರದ ಕೆಲವು ಕಾಲಮ್ಗಳನ್ನು ಪ್ರತಿ ಬಾರಿಯೂ ಶೂನ್ಯಗೊಳಿಸಲು ಪ್ರಾರಂಭಿಸಬೇಕು; ಇತರ ವಾಲ್ಯೂಮ್ ಪ್ರಕಾರಗಳನ್ನು ಯಾವಾಗಲೂ 0 ಗೆ ಹೊಂದಿಸಬೇಕು. ಸಂಯೋಜಿತ ವಿಧಾನದ ಅಗತ್ಯವಿರುವ ಕಾಲಮ್ಗಳೂ ಇವೆ - ಉದಾಹರಣೆಗೆ, cumVolume ಅನ್ನು ಹಿಂದಿನ ನಿಮಿಷದಿಂದ ನಕಲಿಸಬೇಕು ಮತ್ತು ಮೊದಲನೆಯದನ್ನು 0 ಗೆ ಹೊಂದಿಸಬೇಕು. ಈ ಎಲ್ಲಾ ನಿಯತಾಂಕಗಳನ್ನು ನಿಘಂಟು ಡೇಟಾವನ್ನು ಬಳಸಿಕೊಂಡು ಹೊಂದಿಸೋಣ ಪ್ರಕಾರ (ದಾಖಲೆಗೆ ಹೋಲುತ್ತದೆ):
// 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 объяснен ниже
ನಾನು ಅನುಕೂಲಕ್ಕಾಗಿ ನಿಘಂಟಿಗೆ ಸಿಮ್ ಮತ್ತು ಸಮಯವನ್ನು ಸೇರಿಸಿದ್ದೇನೆ, ಈಗ initWith ಅಂತಿಮ ಒಟ್ಟುಗೂಡಿದ ಕೋಷ್ಟಕದಿಂದ ಸಿದ್ಧವಾದ ಸಾಲಾಗಿದೆ, ಅಲ್ಲಿ ಸರಿಯಾದ ಸಿಮ್ ಮತ್ತು ಸಮಯವನ್ನು ಹೊಂದಿಸಲು ಉಳಿದಿದೆ. ಟೇಬಲ್ಗೆ ಹೊಸ ಸಾಲುಗಳನ್ನು ಸೇರಿಸಲು ನೀವು ಇದನ್ನು ಬಳಸಬಹುದು.
ಒಟ್ಟುಗೂಡಿಸುವ ಕಾರ್ಯವನ್ನು ರಚಿಸುವಾಗ ನಮಗೆ aggCols ಅಗತ್ಯವಿದೆ. Q ನಲ್ಲಿನ ಅಭಿವ್ಯಕ್ತಿಗಳನ್ನು ಮೌಲ್ಯಮಾಪನ ಮಾಡುವ ಕ್ರಮದಿಂದಾಗಿ ಪಟ್ಟಿಯನ್ನು ತಲೆಕೆಳಗಾಗಿಸಬೇಕು (ಬಲದಿಂದ ಎಡಕ್ಕೆ). ಕೆಲವು ಕಾಲಮ್ಗಳು ಹಿಂದಿನವುಗಳ ಮೇಲೆ ಅವಲಂಬಿತವಾಗಿರುವುದರಿಂದ ಲೆಕ್ಕಾಚಾರವು ಅಧಿಕದಿಂದ ಕಮ್ವಾಲ್ಯೂಮ್ಗೆ ಹೋಗುವುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳುವುದು ಗುರಿಯಾಗಿದೆ.
ಹಿಂದಿನದರಿಂದ ಹೊಸ ನಿಮಿಷಕ್ಕೆ ನಕಲಿಸಬೇಕಾದ ಕಾಲಮ್ಗಳು, ಅನುಕೂಲಕ್ಕಾಗಿ ಸಿಮ್ ಕಾಲಮ್ ಅನ್ನು ಸೇರಿಸಲಾಗಿದೆ:
rollColumns:`sym`cumVolume;
ಈಗ ಅವುಗಳನ್ನು ಹೇಗೆ ನವೀಕರಿಸಬೇಕು ಎಂಬುದರ ಪ್ರಕಾರ ಕಾಲಮ್ಗಳನ್ನು ಗುಂಪುಗಳಾಗಿ ವಿಂಗಡಿಸೋಣ. ಮೂರು ವಿಧಗಳನ್ನು ಪ್ರತ್ಯೇಕಿಸಬಹುದು:
- ಸಂಚಯಕಗಳು (ಪರಿಮಾಣ, ವಹಿವಾಟು, ..) - ನಾವು ಒಳಬರುವ ಮೌಲ್ಯವನ್ನು ಹಿಂದಿನದಕ್ಕೆ ಸೇರಿಸಬೇಕು.
- ವಿಶೇಷ ಬಿಂದುವಿನೊಂದಿಗೆ (ಹೆಚ್ಚಿನ, ಕಡಿಮೆ, ..) - ನಿಮಿಷದಲ್ಲಿ ಮೊದಲ ಮೌಲ್ಯವನ್ನು ಒಳಬರುವ ಡೇಟಾದಿಂದ ತೆಗೆದುಕೊಳ್ಳಲಾಗುತ್ತದೆ, ಉಳಿದವುಗಳನ್ನು ಕಾರ್ಯವನ್ನು ಬಳಸಿಕೊಂಡು ಲೆಕ್ಕಹಾಕಲಾಗುತ್ತದೆ.
- ಉಳಿದ. ಯಾವಾಗಲೂ ಕಾರ್ಯವನ್ನು ಬಳಸಿಕೊಂಡು ಲೆಕ್ಕಹಾಕಲಾಗುತ್ತದೆ.
ಈ ವರ್ಗಗಳಿಗೆ ಅಸ್ಥಿರಗಳನ್ನು ವ್ಯಾಖ್ಯಾನಿಸೋಣ:
accumulatorCols:`numTrades`volume`pvolume`turnover;
specialCols:`high`low`firstPrice`firstSize;
ಲೆಕ್ಕಾಚಾರದ ಕ್ರಮ
ನಾವು ಒಟ್ಟುಗೂಡಿದ ಕೋಷ್ಟಕವನ್ನು ಎರಡು ಹಂತಗಳಲ್ಲಿ ನವೀಕರಿಸುತ್ತೇವೆ. ದಕ್ಷತೆಗಾಗಿ, ನಾವು ಮೊದಲು ಒಳಬರುವ ಕೋಷ್ಟಕವನ್ನು ಕುಗ್ಗಿಸುತ್ತೇವೆ ಇದರಿಂದ ಪ್ರತಿ ಅಕ್ಷರ ಮತ್ತು ನಿಮಿಷಕ್ಕೆ ಒಂದೇ ಸಾಲು ಇರುತ್ತದೆ. ನಮ್ಮ ಎಲ್ಲಾ ಕಾರ್ಯಗಳು ಹೆಚ್ಚುತ್ತಿರುವ ಮತ್ತು ಸಹಾಯಕವಾಗಿವೆ ಎಂಬ ಅಂಶವು ಈ ಹೆಚ್ಚುವರಿ ಹಂತದ ಫಲಿತಾಂಶವು ಬದಲಾಗುವುದಿಲ್ಲ ಎಂದು ಖಾತರಿಪಡಿಸುತ್ತದೆ. ಆಯ್ಕೆಯನ್ನು ಬಳಸಿಕೊಂಡು ನೀವು ಟೇಬಲ್ ಅನ್ನು ಕುಗ್ಗಿಸಬಹುದು:
select high:max price, low:min price … by sym,time.minute from table
ಈ ವಿಧಾನವು ಅನನುಕೂಲತೆಯನ್ನು ಹೊಂದಿದೆ - ಲೆಕ್ಕ ಹಾಕಿದ ಕಾಲಮ್ಗಳ ಸೆಟ್ ಪೂರ್ವನಿರ್ಧರಿತವಾಗಿದೆ. ಅದೃಷ್ಟವಶಾತ್, Q ನಲ್ಲಿ, ನೀವು ಕ್ರಿಯಾತ್ಮಕವಾಗಿ ರಚಿಸಲಾದ ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳನ್ನು ಬದಲಿಸಬಹುದಾದ ಕಾರ್ಯವಾಗಿ ಆಯ್ಕೆಯನ್ನು ಸಹ ಅಳವಡಿಸಲಾಗಿದೆ:
?[table;whereClause;byClause;selectClause]
ನಮ್ಮ ಪ್ರಕರಣದಲ್ಲಿ ವಾದಗಳ ಸ್ವರೂಪವನ್ನು ನಾನು ವಿವರವಾಗಿ ವಿವರಿಸುವುದಿಲ್ಲ, ಮತ್ತು ಆಯ್ದ ಅಭಿವ್ಯಕ್ತಿಗಳು ಮಾತ್ರ ಕ್ಷುಲ್ಲಕವಾಗಿರುತ್ತವೆ ಮತ್ತು ಅವು ಫಾರ್ಮ್ ಕಾಲಮ್ಗಳ ನಿಘಂಟುಗಳಾಗಿರಬೇಕು! ಹೀಗಾಗಿ, ಕುಗ್ಗಿಸುವ ಕಾರ್ಯವನ್ನು ಈ ಕೆಳಗಿನಂತೆ ವ್ಯಾಖ್ಯಾನಿಸಬಹುದು:
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];
ಸ್ಪಷ್ಟತೆಗಾಗಿ, ನಾನು ಪಾರ್ಸ್ ಫಂಕ್ಷನ್ ಅನ್ನು ಬಳಸಿದ್ದೇನೆ, ಇದು Q ಅಭಿವ್ಯಕ್ತಿಯೊಂದಿಗೆ ಸ್ಟ್ರಿಂಗ್ ಅನ್ನು ಮೌಲ್ಯವಾಗಿ ಪರಿವರ್ತಿಸುತ್ತದೆ ಮತ್ತು ಅದು eval ಫಂಕ್ಷನ್ಗೆ ರವಾನಿಸಬಹುದು ಮತ್ತು ಇದು ಕಾರ್ಯ ಆಯ್ಕೆಯಲ್ಲಿ ಅಗತ್ಯವಿದೆ. ಪ್ರಿಪ್ರೊಸೆಸ್ ಅನ್ನು ಆಯ್ದ ಕಾರ್ಯದ ಪ್ರೊಜೆಕ್ಷನ್ (ಅಂದರೆ, ಭಾಗಶಃ ವ್ಯಾಖ್ಯಾನಿಸಲಾದ ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳೊಂದಿಗಿನ ಕಾರ್ಯ) ಎಂದು ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆ ಎಂಬುದನ್ನು ಗಮನಿಸಿ, ಒಂದು ಆರ್ಗ್ಯುಮೆಂಟ್ (ಟೇಬಲ್) ಕಾಣೆಯಾಗಿದೆ. ನಾವು ಪೂರ್ವಪ್ರಕ್ರಿಯೆಯನ್ನು ಟೇಬಲ್ಗೆ ಅನ್ವಯಿಸಿದರೆ, ನಾವು ಸಂಕುಚಿತ ಕೋಷ್ಟಕವನ್ನು ಪಡೆಯುತ್ತೇವೆ.
ಎರಡನೇ ಹಂತವು ಒಟ್ಟುಗೂಡಿದ ಕೋಷ್ಟಕವನ್ನು ನವೀಕರಿಸುತ್ತಿದೆ. ಮೊದಲು ಅಲ್ಗಾರಿದಮ್ ಅನ್ನು ಸೂಡೊಕೋಡ್ನಲ್ಲಿ ಬರೆಯೋಣ:
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];
…
Q ನಲ್ಲಿ, ಲೂಪ್ಗಳ ಬದಲಿಗೆ ಮ್ಯಾಪ್/ಡಿಡ್ಯೂಸ್ ಫಂಕ್ಷನ್ಗಳನ್ನು ಬಳಸುವುದು ಸಾಮಾನ್ಯವಾಗಿದೆ. ಆದರೆ Q ವೆಕ್ಟರ್ ಭಾಷೆಯಾಗಿರುವುದರಿಂದ ಮತ್ತು ನಾವು ಎಲ್ಲಾ ಕಾರ್ಯಾಚರಣೆಗಳನ್ನು ಒಂದೇ ಬಾರಿಗೆ ಎಲ್ಲಾ ಚಿಹ್ನೆಗಳಿಗೆ ಸುಲಭವಾಗಿ ಅನ್ವಯಿಸಬಹುದು, ನಂತರ ಮೊದಲ ಅಂದಾಜಿಗೆ ನಾವು ಲೂಪ್ ಇಲ್ಲದೆಯೇ ಮಾಡಬಹುದು, ಎಲ್ಲಾ ಚಿಹ್ನೆಗಳ ಮೇಲೆ ಕಾರ್ಯಾಚರಣೆಗಳನ್ನು ಏಕಕಾಲದಲ್ಲಿ ನಿರ್ವಹಿಸಬಹುದು:
idx:calcIdx inputTable;
row:aggTable idx;
aggTable[idx;`high]: row[`high] | inputTable`high;
aggTable[idx;`volume]: row[`volume] + inputTable`volume;
…
ಆದರೆ ನಾವು ಮುಂದೆ ಹೋಗಬಹುದು, Q ವಿಶಿಷ್ಟವಾದ ಮತ್ತು ಅತ್ಯಂತ ಶಕ್ತಿಯುತ ಆಪರೇಟರ್ ಅನ್ನು ಹೊಂದಿದೆ - ಸಾಮಾನ್ಯೀಕೃತ ನಿಯೋಜನೆ ಆಪರೇಟರ್. ಸೂಚ್ಯಂಕಗಳು, ಕಾರ್ಯಗಳು ಮತ್ತು ವಾದಗಳ ಪಟ್ಟಿಯನ್ನು ಬಳಸಿಕೊಂಡು ಸಂಕೀರ್ಣ ಡೇಟಾ ರಚನೆಯಲ್ಲಿ ಮೌಲ್ಯಗಳ ಗುಂಪನ್ನು ಬದಲಾಯಿಸಲು ಇದು ನಿಮ್ಮನ್ನು ಅನುಮತಿಸುತ್ತದೆ. ನಮ್ಮ ಸಂದರ್ಭದಲ್ಲಿ, ಇದು ಈ ರೀತಿ ಕಾಣುತ್ತದೆ:
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;…)];
ದುರದೃಷ್ಟವಶಾತ್, ಟೇಬಲ್ಗೆ ನಿಯೋಜಿಸಲು ನಿಮಗೆ ಸಾಲುಗಳ ಪಟ್ಟಿಯ ಅಗತ್ಯವಿದೆ, ಕಾಲಮ್ಗಳಲ್ಲ, ಮತ್ತು ನೀವು ಫ್ಲಿಪ್ ಕಾರ್ಯವನ್ನು ಬಳಸಿಕೊಂಡು ಮ್ಯಾಟ್ರಿಕ್ಸ್ ಅನ್ನು (ಸಾಲುಗಳ ಪಟ್ಟಿಗೆ ಕಾಲಮ್ಗಳ ಪಟ್ಟಿ) ವರ್ಗಾಯಿಸಬೇಕು. ದೊಡ್ಡ ಟೇಬಲ್ಗೆ ಇದು ದುಬಾರಿಯಾಗಿದೆ, ಆದ್ದರಿಂದ ಬದಲಿಗೆ ನಾವು ಪ್ರತಿ ಕಾಲಮ್ಗೆ ಪ್ರತ್ಯೇಕವಾಗಿ ಸಾಮಾನ್ಯೀಕರಿಸಿದ ನಿಯೋಜನೆಯನ್ನು ಅನ್ವಯಿಸುತ್ತೇವೆ, ನಕ್ಷೆ ಕಾರ್ಯವನ್ನು ಬಳಸಿ (ಅದು ಅಪಾಸ್ಟ್ರಫಿಯಂತೆ ಕಾಣುತ್ತದೆ):
.[aggTable;;:;]'[(idx;)each aggCols; (row[`high] | inputTable`high;row[`volume] + inputTable`volume;…)];
ನಾವು ಮತ್ತೆ ಫಂಕ್ಷನ್ ಪ್ರೊಜೆಕ್ಷನ್ ಅನ್ನು ಬಳಸುತ್ತೇವೆ. Q ನಲ್ಲಿ, ಪಟ್ಟಿಯನ್ನು ರಚಿಸುವುದು ಸಹ ಒಂದು ಕಾರ್ಯವಾಗಿದೆ ಮತ್ತು ಪಟ್ಟಿಗಳ ಪಟ್ಟಿಯನ್ನು ಪಡೆಯಲು ನಾವು ಪ್ರತಿ(ನಕ್ಷೆ) ಕಾರ್ಯವನ್ನು ಬಳಸಿಕೊಂಡು ಅದನ್ನು ಕರೆಯಬಹುದು ಎಂಬುದನ್ನು ಗಮನಿಸಿ.
ಲೆಕ್ಕ ಹಾಕಿದ ಕಾಲಮ್ಗಳ ಸೆಟ್ ಸ್ಥಿರವಾಗಿಲ್ಲ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು, ನಾವು ಮೇಲಿನ ಅಭಿವ್ಯಕ್ತಿಯನ್ನು ಕ್ರಿಯಾತ್ಮಕವಾಗಿ ರಚಿಸುತ್ತೇವೆ. ಒಟ್ಟು ಮತ್ತು ಇನ್ಪುಟ್ ಡೇಟಾವನ್ನು ಉಲ್ಲೇಖಿಸಲು ಸಾಲು ಮತ್ತು inp ವೇರಿಯೇಬಲ್ಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಪ್ರತಿ ಕಾಲಮ್ ಅನ್ನು ಲೆಕ್ಕಾಚಾರ ಮಾಡಲು ಕಾರ್ಯಗಳನ್ನು ಮೊದಲು ವ್ಯಾಖ್ಯಾನಿಸೋಣ:
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");
ಕೆಲವು ಕಾಲಮ್ಗಳು ವಿಶೇಷವಾಗಿರುತ್ತವೆ; ಅವುಗಳ ಮೊದಲ ಮೌಲ್ಯವನ್ನು ಕಾರ್ಯದಿಂದ ಲೆಕ್ಕ ಹಾಕಬಾರದು. ಸಾಲು[`numTrades] ಕಾಲಮ್ನಿಂದ ಇದು ಮೊದಲನೆಯದು ಎಂದು ನಾವು ನಿರ್ಧರಿಸಬಹುದು - ಅದು 0 ಅನ್ನು ಹೊಂದಿದ್ದರೆ, ನಂತರ ಮೌಲ್ಯವು ಮೊದಲನೆಯದು. Q ಆಯ್ದ ಕಾರ್ಯವನ್ನು ಹೊಂದಿದೆ - ?[ಬೂಲಿಯನ್ ಪಟ್ಟಿ;ಪಟ್ಟಿ1;ಪಟ್ಟಿ2] - ಇದು ಮೊದಲ ಆರ್ಗ್ಯುಮೆಂಟ್ನಲ್ಲಿನ ಸ್ಥಿತಿಯನ್ನು ಅವಲಂಬಿಸಿ ಪಟ್ಟಿ 1 ಅಥವಾ 2 ರಿಂದ ಮೌಲ್ಯವನ್ನು ಆಯ್ಕೆ ಮಾಡುತ್ತದೆ:
// high -> ?[isFirst;inp`high;row[`high]|inp`high]
// @ - тоже обобщенное присваивание для случая когда индекс неглубокий
@[`aggExpression;specialCols;{[x;y]"?[isFirst;inp`",y,";",x,"]"};string specialCols];
ಇಲ್ಲಿ ನಾನು ನನ್ನ ಕಾರ್ಯದೊಂದಿಗೆ ಸಾಮಾನ್ಯೀಕರಿಸಿದ ನಿಯೋಜನೆಯನ್ನು ಕರೆದಿದ್ದೇನೆ (ಕರ್ಲಿ ಬ್ರೇಸ್ಗಳಲ್ಲಿನ ಅಭಿವ್ಯಕ್ತಿ). ಇದು ಪ್ರಸ್ತುತ ಮೌಲ್ಯವನ್ನು (ಮೊದಲ ಆರ್ಗ್ಯುಮೆಂಟ್) ಮತ್ತು ಹೆಚ್ಚುವರಿ ಆರ್ಗ್ಯುಮೆಂಟ್ ಅನ್ನು ಪಡೆಯುತ್ತದೆ, ಅದನ್ನು ನಾನು 4 ನೇ ಪ್ಯಾರಾಮೀಟರ್ನಲ್ಲಿ ರವಾನಿಸುತ್ತೇನೆ.
ಬ್ಯಾಟರಿ ಸ್ಪೀಕರ್ಗಳನ್ನು ಪ್ರತ್ಯೇಕವಾಗಿ ಸೇರಿಸೋಣ, ಏಕೆಂದರೆ ಕಾರ್ಯವು ಅವರಿಗೆ ಒಂದೇ ಆಗಿರುತ್ತದೆ:
// volume -> row[`volume]+inp`volume
aggExpression[accumulatorCols]:{"row[`",x,"]+inp`",x } each string accumulatorCols;
ಇದು ಕ್ಯೂ ಮಾನದಂಡಗಳ ಮೂಲಕ ಸಾಮಾನ್ಯ ನಿಯೋಜನೆಯಾಗಿದೆ, ಆದರೆ ನಾನು ಒಂದೇ ಬಾರಿಗೆ ಮೌಲ್ಯಗಳ ಪಟ್ಟಿಯನ್ನು ನಿಯೋಜಿಸುತ್ತಿದ್ದೇನೆ. ಅಂತಿಮವಾಗಿ, ಮುಖ್ಯ ಕಾರ್ಯವನ್ನು ರಚಿಸೋಣ:
// ":",/: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),")]}";
ಈ ಅಭಿವ್ಯಕ್ತಿಯೊಂದಿಗೆ, ನಾನು ಮೇಲೆ ನೀಡಿದ ಅಭಿವ್ಯಕ್ತಿಯನ್ನು ಒಳಗೊಂಡಿರುವ ಸ್ಟ್ರಿಂಗ್ನಿಂದ ಕ್ರಿಯಾತ್ಮಕವಾಗಿ ಕಾರ್ಯವನ್ನು ರಚಿಸುತ್ತೇನೆ. ಫಲಿತಾಂಶವು ಈ ರೀತಿ ಕಾಣುತ್ತದೆ:
{[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])]}
Q ನಲ್ಲಿ ಮೌಲ್ಯಮಾಪನ ಕ್ರಮವು ಬಲದಿಂದ ಎಡಕ್ಕೆ ಇರುವುದರಿಂದ ಕಾಲಮ್ ಮೌಲ್ಯಮಾಪನ ಕ್ರಮವನ್ನು ತಲೆಕೆಳಗಾಗಿಸಲಾಗಿದೆ.
ಈಗ ನಾವು ಲೆಕ್ಕಾಚಾರಗಳಿಗೆ ಅಗತ್ಯವಾದ ಎರಡು ಮುಖ್ಯ ಕಾರ್ಯಗಳನ್ನು ಹೊಂದಿದ್ದೇವೆ, ನಾವು ಸ್ವಲ್ಪ ಮೂಲಸೌಕರ್ಯವನ್ನು ಸೇರಿಸಬೇಕಾಗಿದೆ ಮತ್ತು ಸೇವೆ ಸಿದ್ಧವಾಗಿದೆ.
ಅಂತಿಮ ಹಂತಗಳು
ನಾವು ಎಲ್ಲಾ ಕೆಲಸಗಳನ್ನು ಮಾಡುವ ಪ್ರಿಪ್ರೊಸೆಸ್ ಮತ್ತು ಅಪ್ಡೇಟ್ಆಗ್ ಕಾರ್ಯಗಳನ್ನು ಹೊಂದಿದ್ದೇವೆ. ಆದರೆ ನಿಮಿಷಗಳ ಮೂಲಕ ಸರಿಯಾದ ಪರಿವರ್ತನೆಯನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು ಮತ್ತು ಒಟ್ಟುಗೂಡಿಸುವಿಕೆಗಾಗಿ ಸೂಚ್ಯಂಕಗಳನ್ನು ಲೆಕ್ಕಾಚಾರ ಮಾಡಲು ಇನ್ನೂ ಅವಶ್ಯಕವಾಗಿದೆ. ಮೊದಲಿಗೆ, init ಕಾರ್ಯವನ್ನು ವ್ಯಾಖ್ಯಾನಿಸೋಣ:
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
}
ನಾವು ರೋಲ್ ಕಾರ್ಯವನ್ನು ಸಹ ವ್ಯಾಖ್ಯಾನಿಸುತ್ತೇವೆ, ಅದು ಪ್ರಸ್ತುತ ನಿಮಿಷವನ್ನು ಬದಲಾಯಿಸುತ್ತದೆ:
roll:{[tm]
if[currTime>tm; :init[]]; // если перевалили за полночь, то просто вызовем init
rollCache,::offset _ rollColumns#tradeAgg; // обновим кэш – взять roll колонки из aggTable, обрезать, вставить в rollCache
offset::count tradeAgg;
currSyms::`u#`$();
}
ಹೊಸ ಅಕ್ಷರಗಳನ್ನು ಸೇರಿಸಲು ನಮಗೆ ಒಂದು ಕಾರ್ಯದ ಅಗತ್ಯವಿದೆ:
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)];
}
ಮತ್ತು ಅಂತಿಮವಾಗಿ, ಅಪ್ಡ್ ಫಂಕ್ಷನ್ (Q ಸೇವೆಗಳಿಗಾಗಿ ಈ ಕಾರ್ಯದ ಸಾಂಪ್ರದಾಯಿಕ ಹೆಸರು), ಡೇಟಾವನ್ನು ಸೇರಿಸಲು ಕ್ಲೈಂಟ್ನಿಂದ ಕರೆಯಲ್ಪಡುತ್ತದೆ:
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]; // обновим агрегированную таблицу. Функция ? ищет индекс элементов списка справа в списке слева.
};
ಅಷ್ಟೇ. ನಮ್ಮ ಸೇವೆಯ ಸಂಪೂರ್ಣ ಕೋಡ್ ಇಲ್ಲಿದೆ, ಭರವಸೆ ನೀಡಿದಂತೆ, ಕೆಲವೇ ಸಾಲುಗಳು:
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];
};
ಪರೀಕ್ಷೆ
ಸೇವೆಯ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಪರಿಶೀಲಿಸೋಣ. ಇದನ್ನು ಮಾಡಲು, ನಾವು ಅದನ್ನು ಪ್ರತ್ಯೇಕ ಪ್ರಕ್ರಿಯೆಯಲ್ಲಿ ರನ್ ಮಾಡೋಣ (ಕೋಡ್ ಅನ್ನು service.q ಫೈಲ್ನಲ್ಲಿ ಇರಿಸಿ) ಮತ್ತು init ಕಾರ್ಯವನ್ನು ಕರೆ ಮಾಡಿ:
q service.q –p 5566
q)init[]
ಮತ್ತೊಂದು ಕನ್ಸೋಲ್ನಲ್ಲಿ, ಎರಡನೇ Q ಪ್ರಕ್ರಿಯೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ ಮತ್ತು ಮೊದಲನೆಯದಕ್ಕೆ ಸಂಪರ್ಕಪಡಿಸಿ:
h:hopen `:host:5566
h:hopen 5566 // если оба на одном хосте
ಮೊದಲಿಗೆ, ಚಿಹ್ನೆಗಳ ಪಟ್ಟಿಯನ್ನು ರಚಿಸೋಣ - 10000 ತುಣುಕುಗಳು ಮತ್ತು ಯಾದೃಚ್ಛಿಕ ಕೋಷ್ಟಕವನ್ನು ರಚಿಸಲು ಕಾರ್ಯವನ್ನು ಸೇರಿಸಿ. ಎರಡನೇ ಕನ್ಸೋಲ್ನಲ್ಲಿ:
syms:`IBM`AAPL`GOOG,-9997?`8
rnd:{[n;t] ([] sym:n?syms; time:t+asc n#til 25; price:n?10f; size:n?10)}
ಟೇಬಲ್ನಲ್ಲಿ ಅವುಗಳನ್ನು ಹುಡುಕಲು ಸುಲಭವಾಗುವಂತೆ ನಾನು ಪಟ್ಟಿಗೆ ಮೂರು ನೈಜ ಚಿಹ್ನೆಗಳನ್ನು ಸೇರಿಸಿದ್ದೇನೆ. rnd ಕಾರ್ಯವು n ಸಾಲುಗಳೊಂದಿಗೆ ಯಾದೃಚ್ಛಿಕ ಕೋಷ್ಟಕವನ್ನು ರಚಿಸುತ್ತದೆ, ಅಲ್ಲಿ ಸಮಯವು t ನಿಂದ t+25 ಮಿಲಿಸೆಕೆಂಡುಗಳವರೆಗೆ ಬದಲಾಗುತ್ತದೆ.
ಈಗ ನೀವು ಸೇವೆಗೆ ಡೇಟಾವನ್ನು ಕಳುಹಿಸಲು ಪ್ರಯತ್ನಿಸಬಹುದು (ಮೊದಲ ಹತ್ತು ಗಂಟೆಗಳನ್ನು ಸೇರಿಸಿ):
{h (`upd;`trade;rnd[10000;x])} each `time$00:00 + til 60*10
ಟೇಬಲ್ ಅನ್ನು ನವೀಕರಿಸಲಾಗಿದೆಯೇ ಎಂದು ನೀವು ಸೇವೆಯಲ್ಲಿ ಪರಿಶೀಲಿಸಬಹುದು:
c 25 200
select from tradeAgg where sym=`AAPL
-20#select from tradeAgg where sym=`AAPL
ಫಲಿತಾಂಶ:
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|2919ಸೇವೆಯು ನಿಮಿಷಕ್ಕೆ ಎಷ್ಟು ಡೇಟಾವನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಬಹುದು ಎಂಬುದನ್ನು ಕಂಡುಹಿಡಿಯಲು ಈಗ ಲೋಡ್ ಪರೀಕ್ಷೆಯನ್ನು ಕೈಗೊಳ್ಳೋಣ. ನಾವು ನವೀಕರಣ ಮಧ್ಯಂತರವನ್ನು 25 ಮಿಲಿಸೆಕೆಂಡ್ಗಳಿಗೆ ಹೊಂದಿಸಿದ್ದೇವೆ ಎಂಬುದನ್ನು ನಾನು ನಿಮಗೆ ನೆನಪಿಸುತ್ತೇನೆ. ಅಂತೆಯೇ, ಡೇಟಾವನ್ನು ವಿನಂತಿಸಲು ಬಳಕೆದಾರರಿಗೆ ಸಮಯವನ್ನು ನೀಡಲು ಸೇವೆಯು (ಸರಾಸರಿಯಾಗಿ) ಪ್ರತಿ ನವೀಕರಣಕ್ಕೆ ಕನಿಷ್ಠ 20 ಮಿಲಿಸೆಕೆಂಡುಗಳಿಗೆ ಸರಿಹೊಂದಬೇಕು. ಎರಡನೇ ಪ್ರಕ್ರಿಯೆಯಲ್ಲಿ ಈ ಕೆಳಗಿನವುಗಳನ್ನು ನಮೂದಿಸಿ:
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)}
4800 ಎರಡು ನಿಮಿಷಗಳು. ನೀವು ಪ್ರತಿ 1000 ಮಿಲಿಸೆಕೆಂಡ್ಗಳಿಗೆ 25 ಸಾಲುಗಳಿಗೆ ಮೊದಲು ಓಡಲು ಪ್ರಯತ್ನಿಸಬಹುದು:
start 1000
ನನ್ನ ಸಂದರ್ಭದಲ್ಲಿ, ಫಲಿತಾಂಶವು ಪ್ರತಿ ಅಪ್ಡೇಟ್ಗೆ ಸುಮಾರು ಒಂದೆರಡು ಮಿಲಿಸೆಕೆಂಡ್ಗಳಷ್ಟಿರುತ್ತದೆ. ಹಾಗಾಗಿ ನಾನು ತಕ್ಷಣವೇ ಸಾಲುಗಳ ಸಂಖ್ಯೆಯನ್ನು 10.000 ಕ್ಕೆ ಹೆಚ್ಚಿಸುತ್ತೇನೆ:
start 10000
ಫಲಿತಾಂಶ:
min| 00:00:00.004
avg| 9.191458
med| 9f
max| 00:00:00.030
ಮತ್ತೆ, ವಿಶೇಷ ಏನೂ ಇಲ್ಲ, ಆದರೆ ಇದು ಪ್ರತಿ ನಿಮಿಷಕ್ಕೆ 24 ಮಿಲಿಯನ್ ಸಾಲುಗಳು, ಸೆಕೆಂಡಿಗೆ 400 ಸಾವಿರ. 25 ಮಿಲಿಸೆಕೆಂಡ್ಗಳಿಗಿಂತ ಹೆಚ್ಚು ಕಾಲ, ಅಪ್ಡೇಟ್ ಕೇವಲ 5 ಬಾರಿ ನಿಧಾನವಾಯಿತು, ಸ್ಪಷ್ಟವಾಗಿ ನಿಮಿಷ ಬದಲಾದಾಗ. 100.000 ಕ್ಕೆ ಹೆಚ್ಚಿಸೋಣ:
start 100000
ಫಲಿತಾಂಶ:
min| 00:00:00.013
avg| 25.11083
med| 24f
max| 00:00:00.108
q)sum times
00:02:00.532
ನೀವು ನೋಡುವಂತೆ, ಸೇವೆಯು ಕೇವಲ ನಿಭಾಯಿಸಬಲ್ಲದು, ಆದರೆ ಅದೇನೇ ಇದ್ದರೂ ಅದು ತೇಲುತ್ತಾ ಉಳಿಯಲು ನಿರ್ವಹಿಸುತ್ತದೆ. ಅಂತಹ ಡೇಟಾದ ಪ್ರಮಾಣವು (ನಿಮಿಷಕ್ಕೆ 240 ಮಿಲಿಯನ್ ಸಾಲುಗಳು) ಅತ್ಯಂತ ದೊಡ್ಡದಾಗಿದೆ, ಸೇವೆಯ ಹಲವಾರು ತದ್ರೂಪುಗಳನ್ನು (ಅಥವಾ ಡಜನ್ಗಟ್ಟಲೆ ತದ್ರೂಪುಗಳು) ಪ್ರಾರಂಭಿಸುವುದು ಸಾಮಾನ್ಯವಾಗಿದೆ, ಪ್ರತಿಯೊಂದೂ ಅಕ್ಷರಗಳ ಭಾಗವನ್ನು ಮಾತ್ರ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುತ್ತದೆ. ಆದರೂ, ಪ್ರಾಥಮಿಕವಾಗಿ ಡೇಟಾ ಸಂಗ್ರಹಣೆಯ ಮೇಲೆ ಕೇಂದ್ರೀಕರಿಸುವ ವ್ಯಾಖ್ಯಾನಿತ ಭಾಷೆಗೆ ಫಲಿತಾಂಶವು ಪ್ರಭಾವಶಾಲಿಯಾಗಿದೆ.
ಪ್ರತಿ ನವೀಕರಣದ ಗಾತ್ರದೊಂದಿಗೆ ಸಮಯವು ರೇಖಾತ್ಮಕವಾಗಿ ಏಕೆ ಬೆಳೆಯುತ್ತದೆ ಎಂಬ ಪ್ರಶ್ನೆ ಉದ್ಭವಿಸಬಹುದು. ಕಾರಣವೆಂದರೆ ಕುಗ್ಗಿಸುವ ಕಾರ್ಯವು ವಾಸ್ತವವಾಗಿ C ಕಾರ್ಯವಾಗಿದೆ, ಇದು updateAgg ಗಿಂತ ಹೆಚ್ಚು ಪರಿಣಾಮಕಾರಿಯಾಗಿದೆ. ಒಂದು ನಿರ್ದಿಷ್ಟ ನವೀಕರಣದ ಗಾತ್ರದಿಂದ (ಸುಮಾರು 10.000) ಪ್ರಾರಂಭಿಸಿ, updateAgg ಅದರ ಸೀಲಿಂಗ್ ಅನ್ನು ತಲುಪುತ್ತದೆ ಮತ್ತು ನಂತರ ಅದರ ಕಾರ್ಯಗತಗೊಳಿಸುವ ಸಮಯವು ನವೀಕರಣದ ಗಾತ್ರವನ್ನು ಅವಲಂಬಿಸಿರುವುದಿಲ್ಲ. ಪ್ರಾಥಮಿಕ ಹಂತದ Q ಯ ಕಾರಣದಿಂದಾಗಿ ಸೇವೆಯು ಅಂತಹ ಡೇಟಾವನ್ನು ಜೀರ್ಣಿಸಿಕೊಳ್ಳಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ. ದೊಡ್ಡ ಡೇಟಾದೊಂದಿಗೆ ಕೆಲಸ ಮಾಡುವಾಗ ಸರಿಯಾದ ಅಲ್ಗಾರಿದಮ್ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡುವುದು ಎಷ್ಟು ಮುಖ್ಯ ಎಂಬುದನ್ನು ಇದು ಹೈಲೈಟ್ ಮಾಡುತ್ತದೆ. ಮತ್ತೊಂದು ಅಂಶವೆಂದರೆ ಮೆಮೊರಿಯಲ್ಲಿ ಡೇಟಾದ ಸರಿಯಾದ ಸಂಗ್ರಹಣೆ. ಡೇಟಾವನ್ನು ಸ್ತಂಭಾಕಾರದಲ್ಲಿ ಸಂಗ್ರಹಿಸದಿದ್ದರೆ ಅಥವಾ ಸಮಯಕ್ಕೆ ಆದೇಶಿಸದಿದ್ದರೆ, ನಾವು TLB ಕ್ಯಾಶ್ ಮಿಸ್ನಂತಹ ವಿಷಯದೊಂದಿಗೆ ಪರಿಚಿತರಾಗುತ್ತೇವೆ - ಪ್ರೊಸೆಸರ್ ವಿಳಾಸ ಸಂಗ್ರಹದಲ್ಲಿ ಮೆಮೊರಿ ಪುಟ ವಿಳಾಸದ ಅನುಪಸ್ಥಿತಿ. ವಿಫಲವಾದರೆ ವಿಳಾಸವನ್ನು ಹುಡುಕಲು ಸುಮಾರು 30 ಪಟ್ಟು ಹೆಚ್ಚು ಸಮಯ ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ ಮತ್ತು ಡೇಟಾ ಚದುರಿಹೋದರೆ, ಅದು ಸೇವೆಯನ್ನು ಹಲವಾರು ಬಾರಿ ನಿಧಾನಗೊಳಿಸುತ್ತದೆ.
ತೀರ್ಮಾನಕ್ಕೆ
ಈ ಲೇಖನದಲ್ಲಿ, KDB+ ಮತ್ತು Q ಡೇಟಾಬೇಸ್ ದೊಡ್ಡ ಡೇಟಾವನ್ನು ಸಂಗ್ರಹಿಸಲು ಮತ್ತು ಆಯ್ಕೆಯ ಮೂಲಕ ಸುಲಭವಾಗಿ ಪ್ರವೇಶಿಸಲು ಮಾತ್ರವಲ್ಲ, ನೂರಾರು ಮಿಲಿಯನ್ ಸಾಲುಗಳು / ಗಿಗಾಬೈಟ್ ಡೇಟಾವನ್ನು ಜೀರ್ಣಿಸಿಕೊಳ್ಳಲು ಸಮರ್ಥವಾಗಿರುವ ಡೇಟಾ ಸಂಸ್ಕರಣಾ ಸೇವೆಗಳನ್ನು ರಚಿಸಲು ಸಹ ಸೂಕ್ತವಾಗಿದೆ ಎಂದು ನಾನು ತೋರಿಸಿದೆ. ಒಂದೇ Q ಪ್ರಕ್ರಿಯೆ. Q ಭಾಷೆಯು ಅದರ ವೆಕ್ಟರ್ ಸ್ವಭಾವ, ಅಂತರ್ನಿರ್ಮಿತ SQL ಆಡುಭಾಷೆಯ ಇಂಟರ್ಪ್ರಿಟರ್ ಮತ್ತು ಅತ್ಯಂತ ಯಶಸ್ವಿ ಲೈಬ್ರರಿ ಕಾರ್ಯಗಳ ಕಾರಣದಿಂದಾಗಿ ಡೇಟಾ ಸಂಸ್ಕರಣೆಗೆ ಸಂಬಂಧಿಸಿದ ಅಲ್ಗಾರಿದಮ್ಗಳ ಅತ್ಯಂತ ಸಂಕ್ಷಿಪ್ತ ಮತ್ತು ಪರಿಣಾಮಕಾರಿ ಅನುಷ್ಠಾನಕ್ಕೆ ಅನುಮತಿಸುತ್ತದೆ.
ಮೇಲಿನವು Q ಏನು ಮಾಡಬಹುದು ಎಂಬುದರ ಭಾಗವಾಗಿದೆ ಎಂದು ನಾನು ಗಮನಿಸುತ್ತೇನೆ, ಇದು ಇತರ ವಿಶಿಷ್ಟ ಲಕ್ಷಣಗಳನ್ನು ಹೊಂದಿದೆ. ಉದಾಹರಣೆಗೆ, ಅತ್ಯಂತ ಸರಳವಾದ IPC ಪ್ರೋಟೋಕಾಲ್ ವೈಯಕ್ತಿಕ Q ಪ್ರಕ್ರಿಯೆಗಳ ನಡುವಿನ ಗಡಿಯನ್ನು ಅಳಿಸುತ್ತದೆ ಮತ್ತು ಈ ನೂರಾರು ಪ್ರಕ್ರಿಯೆಗಳನ್ನು ಒಂದೇ ನೆಟ್ವರ್ಕ್ಗೆ ಸಂಯೋಜಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಇದು ಪ್ರಪಂಚದ ವಿವಿಧ ಭಾಗಗಳಲ್ಲಿನ ಡಜನ್ಗಟ್ಟಲೆ ಸರ್ವರ್ಗಳಲ್ಲಿ ನೆಲೆಗೊಂಡಿದೆ.
ಮೂಲ: www.habr.com
