1.
Richard Olsen이라는 이름은 생소합니다. 그런데 이력은 보니까 어떤 분인지 짐작이 갑니다. Retail FX 시장에서 유명한 OANDA의 공동창업자이고 암호통화거래소와 관련한 비지니스를 하고 있는 Lykke Group의 창업자입니다. 물론 관심을 가진 사람은 아닙니다. 몇 일전 Quantnews에 올라온 The Alpha Engine: Designing an Automated Trading Algorithm을 읽으면서 접했습니다. 초록을 보면 30여년전부터 연구해왔던 트레이딩모델이라고 합니다. 81년 발표한 졸업논문이 시작이고 1993년에 발표한 A geographical model for the daily and weekly seasonal volatility in the foreign exchange market 에 뿌리는 두고 있는 듯 합니다. The Alpha Engine: Designing an Automated Trading Algorithm 에 올라온 논문중 Alpha Engine이 기반한 모델입니다.
The building blocks of the efficient trading model include:
– An endogenous time scale, also called intrinsic time, that dissects the price curve continuously and allows for determination of position sizes by identifying market activity that deviates from normal behavior. Rather that action based on a specific time, action is more accurately measured by events.
– Patterns, called scaling laws (also observed in physics, biology, earth and planetary sciences, economics, finance, etc.), that measure an immense number of natural processes and adjust for them.
– Skewing of cascading and de-cascading coastline innovation to mitigate the risk of large inventory size during trending markets.
– Introduction of asymmetric thresholds by eliminating static and rigid guide points that arbitrarily limit directional change thresholds.
위 모델을 자세히 설명한 3장 Guided by an Event-Based Framework을 보면 세가지 개념을 설명합니다.
첫째는 Intrinsic Time.
둘째는 Directional Change
셋째는 Scaling Law
셋중 가장 생소한 개념이 Intrinsic Time입니다. Intrinsic은 파생상품을 설명할 때 자주 등장하는 내재적 가치(Intrinsic value)의 ‘내재적’으로 번역하는 단어입니다. 다른 논문에서 보지 못한 개념이 등장입니다. 내재적 시간으로 해석할 수 있지만 저자가 말하고자 하는 바를 온전히 담지 못합니다. 그래서 시간을 표현할 때 사람들이 언급하는 두가지 개념, 카이로스와 크로노스로 이해하면 어떨까 합니다.
그리스어에는 “시간”을 뜻하는 단어가 두 종류가 있습니다. “크로노스 Chronos”와 “카이로스 Kairos”가 바로 그 것이지요. “크로노스”는 자신의 자녀를 다 먹어 치웠던 원시 시대의 신神을 가르킵니다. 따라서 “크로노스”는 우리를 집어삼키는 시간, 곧 우리가 쫓기듯 보내는 시간, 이런저런 일을 더 빨리 처리하도록 재촉받는 시간이라 할 수 있습니다. “재촉하다”라는 뜻을 지닌 독일어 단어 “헷첸 hetzen”은 “미워하다”라는 뜻을 지닌 독일어 단어 “하센 hassen”에서 왔습니다. 이런저런 일을 기한 내에 처리하도록 자신을 재촉하는 것은 결국 자기 자신을 미워하는 행위로, “크로노스”는 곧 자기 증오의 시간으로 해석할 수 있습니다…. 이에 반해 유쾌한 시간을 가리키는 “카이로스”가 있습니다…. 다른 한 편으로, “카이로스”는 “꼭 알맞은 순간”을 뜻합니다. 카이로스는 앞머리에 머리카락이 풍성하기에 제때라면 쉽게 붙잡을 수 있지만 뒤통수에는 머리카락이 하나도 없기에 지나간 뒤에는 잡을 수가 없지요. 이 비유를 통해 그리스인들은 기회를 제 때 잡아야 한다는 점을 말하려 했습니다.
시간에 대한 두 관점 : 카이로스 Kairos와 크로노스 chronos중에서
Intrinsic Time은 투자자를 중심으로 판단하는 시간입니다. 투자라는 내적인 가치를 온전히 담아내는 시간이라는 뜻입니다. 전통적으로 많이 사용하는 시계열 데이타는 일정한 기준으로 일정한 시간(Interval)단위로 정의한 데이타인데 Intinsic Time은 투자자가 보유하고 있는 자산의 손익에 영향을 주는 요소(Element, Event)을 기준으로 한 시간을 사용하자고 합니다.
A time series is a series of data points indexed (or listed or graphed) in time order. Most commonly, a time series is a sequence taken at successive equally spaced points in time. Thus it is a sequence of discrete-time data.
논문 3장을 보면 아래와 같이 설명합니다. 이벤트에 기반한 접근법입니다.
We all experience time as a fundamental and unshakable part of reality. In stark contrast, the philosophy of time and the notion of time in fundamental physics challenges our mundane perception of it. In an operational definition, time is simply what instruments measure and register. In this vein, we understand the passage of time in financial time series as a set of events, i.e., system interactions. In this novel time ontology, time ceases to exist between events. In contrast to the continuity of physical time, now only interactions, or events, let a system’s clock tick. Hence this new methodology is called intrinsic time. This event-based approach opens the door to a modelling framework that yields selfreferential behavior which does not rely on static building blocks and has a dynamic frame of reference.
논문의 저자인 Olsen이 대표로 있는 Lykke의 Pairs trading in forex market with respect to intrinsic time를 보면 Intrinsic Time을 이렇게 정의합니다.
Intrinsic Time
At Lykke, we’re in the process of redefining the way how time is looked upon in financial markets. Leading to a long-needed paradigm change, namely from observed physical time to an activity based timescale, called intrinsic time. In contrast to the continuity of physical time, now only interactions, or events, let a system’s clock tick. This event-driven paradigm defines focal points by blurring out irrelevant details of the price evolution with the help of defining fixed event thresholds of different sizes.
With these events, every price curve can be dissected into components that represent a change in the price trend (Directional Change) and a trend component (Overshoot). For a Directional Change to be detected, first an initial direction mode needs to be chosen. As an example, in an up mode, an increasing price move will result in the extremal price being updated and continuously increased. If the price goes down, the difference between the extremal price and the current price is evaluated. If this distance (in percent) exceeds the predefined Directional Change threshold, a Directional Change is registered. Now the mode is switched to down and the algorithm continues correspondingly.
2011년에 발표한 A Directional-Change Events Approach for Studying Financial Time Series은 Intrinsic Time을 다음과 같이 정의합니다.
Intrinsic time adopts an event-based system in contrast to physical time which adopts a point-based system. In intrinsic time, time is defined by events.
Intrinsic Time을 코드로 표현하면 어떻게 될까요? Intrinsic time for cryptocurrency data 이 소개한 Instrinic Time의 개념을 R 코드화한 소스입니다. 이벤트접근법이라고 했는데 코드를 보면 이벤트를 2%의 가격등락입니다. 이를 기준으로 시계열데이타를 재생합니다.
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 |
dc_intervals <- function(dat, threshold=0.02/100, price="price", time="time", vol="amount") { # candidates for confirmed directional change dcc_up_cand <- c(FALSE, c(dat[2:nrow(dat), price]<dat[1:(nrow(dat)-1), price]*(1-threshold))) dcc_down_cand <- c(FALSE, c(dat[2:nrow(dat), price]>dat[1:(nrow(dat)-1), price]*(1+threshold))) # initialize new columns dat[,"interval"] <- NA # 4 types of intervals: down, down_os, up, up_os (os=overshoot, NA means unknown) dat[,"point"] <- NA # 3 types of points: dcc_up, dcc_down, ep (NA means ignore) # threshold too large? if(all(!dcc_up_cand)) stop("threshold=",threshold, " too large, no price moves above this threshold") if(all(!dcc_down_cand)) stop("threshold=",threshold, " too large, no price moves above this threshold") # figure out if the first change is up or down first_up <- which.max(dcc_up_cand) first_down <- which.max(dcc_down_cand) if(first_up<first_down) { dat[first_up,"point"] <- "dcc_up" mode <- "dcc_up" i <- first_up } else { dat[first_down, "point"] <- "dcc_down" mode <- "dcc_down" i <- first_down } # iterate till the end of the data while (i<nrow(dat)) { if(mode=="dcc_up") { j <- which.max(dcc_down_cand[i+1:length(dcc_down_cand)])+i if(!dcc_down_cand[j]) break # end of dataset dat[i,"point"] <- "dcc_up" dat[i,"interval"] <- "uoi" if((j-1)-(i+1)>0) { # if not changing direction immediately ep_idx <- which.min(dat[(i+1):(j-1), price])+i dat[ep_idx, "point"] <- "ep" dat[i:ep_idx-1, "interval"] <- "uoi" dat[ep_idx:j,"interval"] <- "dei" } i <- j+1 mode <- "dcc_down" } else { # mode=="dcc_down" j <- which.max(dcc_up_cand[i+1:length(dcc_up_cand)])+i if(!dcc_up_cand[j]) break # end of dataset dat[i,"point"] <- "dcc_down" dat[i,"interval"] <- "doi" if((j-1)-(i+1)>0) { # if not changing direction immediately ep_idx <- which.max(dat[(i+1):(j-1), price])+i dat[ep_idx, "point"] <- "ep" dat[i:ep_idx-1, "interval"] <- "doi" dat[ep_idx:j,"interval"] <- "uei" } i <- j+1 mode <- "dcc_up" } } dat <- dat[!is.na(dat[,"point"]),] # drop all points that are not dcc points return(dat) } ohlc_summary <- function(dat, browse=TRUE, verbose=TRUE) { dat$hour <- strftime(dat$time, format="%Y-%m-%d %H") one_hour <- function(dset) { i1 <- which.min(dset$time) i2 <- which.max(dset$time) ans <- data.frame( time=strptime(dset[1,"hour"], format="%Y-%m-%d %H"), open=dset[i1, "price"], hi=max(dset$price), lo=min(dset$price), close=dset[i2,"price"], amount=sum(dset$amount) ) return(ans) } ret <- do.call(rbind, lapply(split(dat, dat$hour), FUN=one_hour)) if(verbose) message("ohlc summary built.") if(browse) browser() return(ret) } # begin, end ... "YYYY-MM-DD" plot_dc <- function(dc_dat=NULL, ohlc=NULL, ticks=NULL, begin=NULL, end=NULL, lwd=3, price="price", time="time", vol="amount", ...) { if(is.null(ohlc) & !is.null(ticks)) ohlc <- ohlc_summary(ticks) if(is.null(dc_dat) & !is.null(ticks)) dc_dat <- dc_intervals(dat=ticks, time=time, price=price, vol=vol) if(is.null(dc_dat)) stop("missing dc data.") if(is.null(end)) end <- max(dc_dat[,time]) if(is.character(end)) end <- as.POSIXct(strptime(end, "%Y-%m-%d")) if(is.null(begin)) begin <- min(dc_dat[,time]) if(is.character(begin)) begin <- as.POSIXct(strptime(begin, "%Y-%m-%d")) plot( dat=1, col="white", dc_dat=dc_dat, xlab="", xlim=c(min(ohlc$time), max(ohlc$time)), ylim=c(min(ticks$price), max(ticks$price)), main=sprintf("%.2f%% Direction Changes for BTC", threshold*100), cex.main=1 ) if(!is.null(ohlc)) { for (i in seq(1, nrow(ohlc))) { segments( x0=ohlc[i,"time"], y0=ohlc[i,"lo"], x1=ohlc[i,"time"], y0=ohlc[i, "hi"], col="darkgrey", lwd=0.5 ) } } dcc_idx <- which(dc_dat$point=="dcc_up"|dc_dat$point=="dcc_down") for (i in setdiff(dcc_idx,1)) { segments( x0=dc_dat[i-1,time], y0=dc_dat[i-1,price], x1=dc_dat[i,time], y1=dc_dat[i,price], col="red", lwd=lwd ) } for (i in setdiff(dcc_idx,nrow(dc_dat))) { segments( x0=dc_dat[i,time], y0=dc_dat[i,price], x1=dc_dat[i+1,time], y1=dc_dat[i+1,price], col="darkgreen", lwd=lwd ) } axis(1,at=dc_dat[dcc_idx, time],labels=FALSE) } |
2.
Directional Change Project라고 하는 생소한 프로젝트가 있습니다. 논문 저자인 Olsen과 Lykke가 후원하는 프로젝트입니다. 여기서 정의하는 DC입니다.
Market prices are traditionally sampled in fixed time-intervals to form time series. Directional change (DC) is an alternative approach to record price movements. DC is data-driven: price changes dictate when a price is sampled and recorded. DC allows us to observe features in data that may not be observable in time series. Time series and DC-based summaries should complement each other: they allow us to see data from two different angles (like seeing things with two eyes instead of one). Our research agenda is to develop ways under DC to turn data into information, knowledge and applications.
단순히 생각해 보면 Trend Following이라고 할 때의 Trend (Momentum)와 헷갈립니다. Event와 Monemtum의 차이인데 솔직히 미묘합니다.
What is Trend Trading
Trend trading is a trading strategy that attempts to capture gains through the analysis of a security’s momentum in a particular direction.
What Is Momentum?
Momentum is the rate of acceleration of a security’s price or volume – that is, the speed at which the price is changing. Simply put, it refers to the rate of change on price movements for a particular asset and is usually defined as a rate. In technical analysis, momentum is considered an oscillator and is used to help identify trend lines.
앞서 Directional Change Project와 SSRN에 올라온 R. Olsen의 논문을 보면 오랜 동안 다양한 영역에서 검증하려고 노력한 흔적이 역력합니다.
Agent-Based Model in Directional-Change Intrinsic Time
Instantaneous Volatility Seasonality of Bitcoin in Directional-Change Intrinsic Time
이상을 보면 Directional Change는 Intrinsic Time과 짝을 이루는 개념입니다. 그래서 directional-change intrinsic time 라고 표현으로 사용합니다.
3.
마지막으로 제안한 개념이 Scaling Law입니다. 스케일(SCALE) : 생물 도시 기업의 성장과 죽음에 관한 보편 법칙이 말하는 스케일의 법칙입니다.
저자는 생물학을 공부하다 ‘스케일링의 법칙’을 발견했다. 생물의 크기 변화에서 발견되는 규모 증감의 법칙이다. 어떤 동물의 몸집이 다른 동물보다 2배 크다면, 필요로 하는 에너지양은 75% 늘어난다. 즉 크기가 2배 늘어날 때마다 25%의 에너지가 절약된다. 몸집의 차가 커질수록 에너지 효율의 격차는 더 벌어진다. 예를 들어 코끼리는 쥐보다 1만배 무겁지만, 필요한 에너지의 양은 쥐의 1000배면 된다. 코끼리의 에너지 효율이 쥐보다 10배 더 좋은 셈이다. 이른바 ‘규모의 경제’가 나타나는 것이다.
앞서 DC Intrinsic Time을 이용하여 스케일의 법칙을 발견하는 것이고 이것이 투자전략의 기초를 이룹니다.
Scaling-law relations characterize an immense number of natural processes, prominently in the form of
1. scaling-law distributions;
2. scale-free networks;
3. cumulative relations of stochastic processes.Scaling-law relations display scale invariance because scaling the function’s argument x preserves the shape of the function f(x) [27]. Measurements of scaling-law processes yield values distributed across an enormous dynamic range, and for any section analysed, the proportion of small to large events stays constant.
이를 FX시장에 적용한 Patterns in high-frequency FX data:Discovery of 12 empirical scaling laws입니다. 12가지 패턴을 발견했다고 합니다.
Olsen이 이를 교과서식으로 정리한 High Frequency Finance: Using Scaling Laws to Build Trading Models을
4.
이상에서 설명한 논문의 모델 (Framework)는 Github에서 확인할 수 있습니다. 논문의 공저자인 Anton Golub가 만든 The Alpha Engine: Designing an Automated Trading Algorithm입니다.
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 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 |
/* * The following code is intended to give the reader an overview of how the * trading model algorithm functions. However, the focus lies not on the framework * supporting the trading model. As a result, users are required to implement some * of their own code in order to get the trading model running. Specifically, this * concerns your price feed data and the take profit target. The only configuration * is the number and sizes of the thresholds on which the Coastline Traders live. * See all the TODO in the code. — */ import java.io.FileWriter; import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Random; public class Code { // Main method public static void main(String[] args) { new Code(); } // Run algo public Code(){ // TODO Currency configuration String[] ccyList = {"AUD_CAD"};/* , "AUD_JPY", "AUD_NZD", "AUD_USD", "CAD_JPY", "CHF_JPY", "EUR_AUD", "EUR_CAD", "EUR_CHF", "EUR_GBP", "EUR_JPY", "EUR_NZD", "EUR_USD", "GBP_AUD", "GBP_CAD", "GBP_CHF", "GBP_JPY", "GBP_USD", "NZD_CAD", "NZD_JPY", "NZD_USD", "USD_CAD", "USD_CHF", "USD_JPY"}; */ int length = ccyList.length; FXrateTrading[] trading = new FXrateTrading[length]; // TODO Threshold configuration (see below) double[] deltaS = {0.25/100.0, 0.5/100.0, 1.0/100.0, 1.5/100.0}; for( int i = 0; i < length; ++i ){ trading[i] = new FXrateTrading(ccyList[i], deltaS.length, deltaS); } // Run PriceFeedData p = new PriceFeedData(); for( int i = 0; i < length; ++i ){ trading[i].runTradingAsymm(p); } } public class Runner{ public double prevExtreme; public long prevExtremeTime; public double prevDC; public long prevDCTime; public double extreme; public long extremeTime; public double deltaUp; public double deltaDown; public double deltaStarUp, deltaStarDown; public double osL; public int type; public boolean initalized; public double reference; public String fileName; public Runner(double threshUp, double threshDown, PriceFeedData price, String file, double dStarUp, double dStarDown){ prevExtreme = price.elems.mid; prevExtremeTime = price.elems.time; prevDC = price.elems.mid; prevDCTime = price.elems.time; extreme = price.elems.mid; extremeTime = price.elems.time; reference = price.elems.mid; type = -1; deltaUp = threshUp; deltaDown = threshDown; osL = 0.0; initalized = true; fileName = new String(file); deltaStarUp = dStarUp; deltaStarDown = dStarDown; } public Runner(double threshUp, double threshDown, double price, String file, double dStarUp, double dStarDown){ prevExtreme = price; prevExtremeTime = 0; prevDC = price; prevDCTime = 0; extreme = price; extremeTime = 0; reference = price; deltaStarUp = dStarUp; deltaStarDown = dStarDown; type = -1; deltaUp = threshUp; deltaDown = threshDown; osL = 0.0; initalized = true; fileName = new String(file); } public Runner(double threshUp, double threshDown, String file, double dStarUp, double dStarDown){ deltaUp = threshUp; deltaDown = threshDown; initalized = false; fileName = new String(file); deltaStarUp = dStarUp; deltaStarDown = dStarDown; } public int run(PriceFeedData price){ if( price == null ) return 0; if( !initalized ){ type = -1; osL = 0.0; initalized = true; prevExtreme = price.elems.mid; prevExtremeTime = price.elems.time; prevDC = price.elems.mid; prevDCTime = price.elems.time; extreme = price.elems.mid; extremeTime = price.elems.time; reference = price.elems.mid; return 0; } if( type == -1 ){ if( Math.log(price.elems.bid/extreme) >= deltaUp ){ prevExtreme = extreme; prevExtremeTime = extremeTime; type = 1; extreme = price.elems.ask; extremeTime = price.elems.time; prevDC = price.elems.ask; prevDCTime = price.elems.time; reference = price.elems.ask; return 1; } if( price.elems.ask < extreme ){ extreme = price.elems.ask; extremeTime = price.elems.time; osL = -Math.log(extreme/prevDC)/deltaDown; if( Math.log(extreme/reference) <= -deltaStarUp ){ reference = extreme; return -2; } return 0; } } else if( type == 1 ){ if( Math.log(price.elems.ask/extreme) <= -deltaDown ){ prevExtreme = extreme; prevExtremeTime = extremeTime; type = -1; extreme = price.elems.bid; extremeTime = price.elems.time; prevDC = price.elems.bid; prevDCTime = price.elems.time; reference = price.elems.bid; return -1; } if( price.elems.bid > extreme ){ extreme = price.elems.bid; extremeTime = price.elems.time; osL = Math.log(extreme/prevDC)/deltaUp; if( Math.log(extreme/reference) >= deltaStarDown ){ reference = extreme; return 2; } return 0; } } return 0; } public int run(double price){ if( !initalized ){ type = -1; osL = 0.0; initalized = true; prevExtreme = price; prevExtremeTime = 0; prevDC = price; prevDCTime = 0; extreme = price; extremeTime = 0; reference = price; return 0; } if( type == -1 ){ if( price - extreme >= deltaUp ){ prevExtreme = extreme; prevExtremeTime = extremeTime; type = 1; extreme = price; extremeTime = 0; prevDC = price; prevDCTime = 0; reference = price; osL = 0.0; return 1; } if( price < extreme ){ extreme = price; extremeTime = 0; osL = -(extreme - prevDC); if( extreme - reference <= -deltaStarUp ){ reference = extreme; return -2; } return 0; } } else if( type == 1 ){ if( price - extreme <= -deltaDown ){ prevExtreme = extreme; prevExtremeTime = extremeTime; type = -1; extreme = price; extremeTime = 0; prevDC = price; prevDCTime = 0; reference = price; osL = 0.0; return 1; } if( price > extreme ){ extreme = price; extremeTime = 0; osL = (extreme -prevDC); if( extreme - reference >= deltaStarDown ){ reference = extreme; return 2; } return 0; } } return 0; } } public class Liquidity{ public class Runner{ public double prevDC; public double extreme; public double deltaUp; public double deltaDown; public int type; public boolean initalized; public String fileName; public Runner(double threshUp, double threshDown, PriceFeedData price, String file){ prevDC = price.elems.mid; extreme = price.elems.mid; type = -1; deltaUp = threshUp; deltaDown = threshDown;initalized = true; fileName = new String(file); } public Runner(double threshUp, double threshDown, double price, String file){ prevDC = price; extreme = price; type = -1; deltaUp = threshUp; deltaDown = threshDown; initalized = true; fileName = new String(file); } public Runner(double threshUp, double threshDown, String file){ deltaUp = threshUp; deltaDown = threshDown; initalized = false; fileName = new String(file); } public int run(PriceFeedData price){ if( price == null ) return 0; if( !initalized ){ type = -1; initalized = true; prevDC = price.elems.mid; extreme = price.elems.mid; return 0; } if( type == -1 ){ if( Math.log(price.elems.bid/extreme) >= deltaUp ){ type = 1; extreme = price.elems.ask; prevDC = price.elems.ask; return 1; } if( price.elems.ask < extreme ){ extreme = price.elems.ask; return 0; } } else if( type == 1 ){ if( Math.log(price.elems.ask/extreme) <= -deltaDown ){ type = -1; extreme = price.elems.bid; prevDC = price.elems.bid; return -1; } if( price.elems.bid > extreme ){ extreme = price.elems.bid; return 0; } } return 0; } public int run(double price){ if( !initalized ){ type = -1; initalized = true; prevDC = price; extreme = price; return 0; } if( type == -1 ){ if( price - extreme >= deltaUp ){ type = 1; extreme = price; prevDC = price; return 1; } if( price < extreme ){ extreme = price; return 0; } } else if( type == 1 ){ if( price - extreme <= -deltaDown ){ type = -1; extreme = price; prevDC = price; ; return 1; } if( price > extreme ){ extreme = price; return 0; } } return 0; } } public Runner[] runner; double[] prevState; double surp = 0.0, dSurp = 0.0, uSurp = 0.0; double liquidity, liquidityUp, liquidityDown; double liqEMA; double upLiq, downLiq, diffLiq, diffRaw; double H1 = 0.0, H2 = 0.0; double d1 = 0.0, d2 = 0.0; double alpha, alphaWeight; List<Double> mySurprise, downSurprise, upSurprise; public Liquidity(){}; @SuppressWarnings("deprecation") public Liquidity(PriceFeedData price, double delta1, double delta2, int lgt){ double prob = Math.exp(-1.0); H1 = -(prob*Math.log(prob) + (1.0 - prob)*Math.log(1.0 - prob)); H2 = prob*Math.pow(Math.log(prob), 2.0) + (1.0 - prob)*Math.pow(Math.log(1.0 - prob), 2.0) - H1*H1; runner = new Runner[lgt]; prevState = new double[lgt]; d1 = delta1; d2 = delta2; getH1nH2(); //skip computation and assign! runner = new Runner[lgt]; prevState = new double[lgt]; for( int i = 0; i < runner.length; ++i ){ runner[i] = new Runner(0.025/100.0 + 0.05/100.0*(double)i, 0.025/100.0 + 0.05/100.0*(double)i, price, "JustFake"); runner[i].type = (i%2 == 0 ? 1 : -1); prevState[i] = (runner[i].type == 1 ? 1 : 0); } surp = H1; dSurp = H1; uSurp = H1; liquidity = 0.5; liqEMA = 0.5; mySurprise = new LinkedList<Double>(); downSurprise = new LinkedList<Double>(); upSurprise = new LinkedList<Double>(); for( int i = 0; i < 100; ++i ){ mySurprise.add(new Double(H1)); downSurprise.add(new Double(H1)); upSurprise.add(new Double(H1)); } //computeLiquidity(); downLiq = 0.5; upLiq = 0.5; diffLiq = 0.5; diffRaw = 0.0; alpha = 2.0/(100.0 + 1.0); alphaWeight = Math.exp(-alpha); } public void getH1nH2(){ double price = 0.0; alpha = 2.0/(100.0 + 1.0); alphaWeight = Math.exp(-alpha); runner = new Runner[runner.length]; for( int i = 0; i < runner.length; ++i ){ runner[i] = new Runner(0.025/100.0 + 0.05/100.0*(double)i, 0.025/100.0 + 0.05/100.0*(double)i, price, "JustFake"); runner[i].type = (i%2 == 0 ? 1 : -1); prevState[i] = (runner[i].type == 1 ? 1 : 0); } double total1 = 0.0, total2 = 0.0; Random rand = new Random(1); double dt = 1.0/Math.sqrt(1000000.0); double sigma = 0.25; // 25% for( int i = 0; i < 100000000; ++i ){ price += sigma*dt*rand.nextGaussian(); for( int j= 0; j < runner.length; ++j ){ if( Math.abs(runner[j].run(price)) == 1 ){ // this is OK for simulated prices double myProbs = getProbs(j); total1 = total1*alphaWeight + (1.0 - alphaWeight)*(-Math.log(myProbs)); total2 = total2*alphaWeight + (1.0 - alphaWeight)*Math.pow(Math.log(myProbs), 2.0); } } } H1 = total1; H2 = total2 - H1*H1; System.out.println("H1:" + H1 + " H2:" + H2); } @SuppressWarnings("deprecation") public boolean Trigger(PriceFeedData price){ // -- update values -- boolean doComp = false; for( int i = 0; i < runner.length; ++i ){ int value = runner[i].run(price); if( Math.abs(value) == 1 ){ //double alpha = 2.0/(100.0 + 1.0); double myProbs = getProbs(i); surp = surp*alphaWeight + (1.0 - alphaWeight)*(-Math.log(myProbs)); mySurprise.remove(0); mySurprise.add(new Double(-Math.log(myProbs))); if( runner[i].type == -1 ){ dSurp = dSurp*alphaWeight + (1.0 - alphaWeight)*(-Math.log(myProbs)); downSurprise.remove(0); downSurprise.add(new Double(-Math.log(myProbs))); }else if( runner[i].type == 1 ){ uSurp = uSurp*alphaWeight + (1.0 - alphaWeight)*(-Math.log(myProbs)); upSurprise.remove(0); upSurprise.add(new Double(-Math.log(myProbs))); } doComp = true; } } if( doComp ){ liqEMA = (1.0 - CumNorm(Math.sqrt(100.0)*(surp - H1)/Math.sqrt(H2))); upLiq = (1.0 - CumNorm(Math.sqrt(100.0)*(uSurp - H1)/Math.sqrt(H2))); downLiq = (1.0 - CumNorm(Math.sqrt(100.0)*(dSurp - H1)/Math.sqrt(H2))); diffLiq = CumNorm(Math.sqrt(100.0)*(uSurp - dSurp)/Math.sqrt(H2)); diffRaw = Math.sqrt(100.0)*(uSurp-dSurp)/Math.sqrt(H2); } return doComp; } public double getProbs(int i){ int where = -1; for( int j = 1; j < prevState.length; ++j ){ if( prevState[j] != prevState[0] ){ where = j; break; } } if( i > 0 && where != i ){ System.out.println("This should not happen! " + where); } prevState[i] = (prevState[i] == 1 ? 0 : 1); if( where == 1 ){ if( i > 0 ){ return Math.exp(-(runner[1].deltaDown - runner[0].deltaDown)/runner[0].deltaDown); }else{ return (1.0 - Math.exp(-(runner[1].deltaDown - runner[0].deltaDown)/runner[0].deltaDown)); } }else if( where > 1 ){ double numerator = 0.0; for( int k = 1; k <= where; ++k ){ numerator -= (runner[k].deltaDown - runner[k-1].deltaDown)/runner[k-1].deltaDown; } numerator = Math.exp(numerator); double denominator = 0.0; for( int k = 1; k <= where - 1; ++k ){ double secVal = 0.0; for( int j = k+1; j <= where; ++j ){ secVal -= (runner[j].deltaDown - runner[j-1].deltaDown)/runner[j-1].deltaDown; } denominator += (1.0 - Math.exp(-(runner[k].deltaDown - runner[k-1].deltaDown)/runner[k-1].deltaDown))*Math.exp(secVal); } if( i > 0 ){ return numerator/(1.0 - denominator); }else{ return (1.0 - numerator/(1.0 - denominator)); } } else{ return 1.0; } } // Another implementation of the CNDF for a standard normal: N(0,1) double CumNorm(double x){ // Protect against overflow if (x > 6.0) return 1.0; if (x < -6.0) return 0.0; double b1 = 0.31938153; double b2 = -0.356563782; double b3 = 1.781477937; double b4 = -1.821255978; double b5 = 1.330274429; double p = 0.2316419; double c2 = 0.3989423; double a = Math.abs(x); double t = 1.0 / (1.0 + a * p); double b = c2*Math.exp((-x)*(x/2.0)); double n = ((((b5*t+b4)*t+b3)*t+b2)*t+b1)*t; n = 1.0-b*n; if ( x < 0.0 ) n = 1.0 - n; return n; } public boolean computeLiquidity(long deltaT){ double surpT = 0.0; double downSurp = 0.0, upSurp = 0.0; for( int i = 0; i < mySurprise.size(); ++i ){ surpT += mySurprise.get(i).doubleValue(); downSurp += downSurprise.get(i).doubleValue(); upSurp += upSurprise.get(i).doubleValue(); } liquidity = 1.0 - CumNorm((surpT - H1*mySurprise.size())/Math.sqrt(H2*mySurprise.size())); liquidityDown = 1.0 - CumNorm((downSurp - H1*downSurprise.size())/Math.sqrt(H2*downSurprise.size())); liquidityUp = 1.0 - CumNorm((upSurp - H1*upSurprise.size())/Math.sqrt(H2*upSurprise.size())); return true; } }; public class HelpClass{ long time; double price; double liq; public HelpClass(){}; public HelpClass(long t, double p, double l){ time = t; price = p; liq = l; } }; public class Prices{ double bid; double ask; Prices(){}; Prices(Prices p){ bid = p.bid; ask = p.ask; } Prices(double b, double a){ bid = b; ask = a; } }; public class NbDcCounter{ List<Long> eventList; double delta; long timeWindow; Runner runner; NbDcCounter(){}; NbDcCounter(double d, long tW){ eventList = new LinkedList<Long>(); delta = d; runner = new Runner(delta, delta, "events", delta, delta); timeWindow = tW; } @SuppressWarnings("deprecation") boolean run(PriceFeedData price){ if( Math.abs(runner.run(price)) == 1 ){ eventList.add(new Long(price.elems.time)); } if( eventList.size() == 0 ) return true; while( eventList.get(0).longValue() < price.elems.time - timeWindow ) eventList.remove(0); return true; } }; public class LocalLiquidity{ double deltaUp, deltaDown; double delta; double extreme, dStar, reference; int type; boolean initalized; double surp, upSurp, downSurp; double liq, upLiq, downLiq; double alpha, alphaWeight; double H1, H2; LocalLiquidity(){}; LocalLiquidity(double d, double dUp, double dDown, double dS, double a){ type = -1; deltaUp = dUp; deltaDown = dDown; dStar = dS; delta = d; initalized = false; alpha = a; alphaWeight = Math.exp(-2.0/(a + 1.0)); computeH1H2exp(dS); } LocalLiquidity(double d,double dUp, double dDown, PriceFeedData price, double dS, double a){ deltaUp = dUp; deltaDown = dDown; delta = d; type = -1; extreme = reference = price.elems.mid; dStar = dS; initalized = true; alpha = a; alphaWeight = Math.exp(-2.0/(a + 1.0)); computeH1H2exp(dS); } boolean computeH1H2exp(double dS){ H1 = -Math.exp(-dStar/delta)*Math.log(Math.exp(-dStar/delta)) - (1.0 - Math.exp(-dStar/delta))*Math.log(1.0 - Math.exp(-dStar/delta)); H2 = Math.exp(-dStar/delta)*Math.pow(Math.log(Math.exp(-dStar/delta)), 2.0) - (1.0 - Math.exp(-dStar/delta))*Math.pow(Math.log(1.0 - Math.exp(-dStar/delta)), 2.0) - H1*H1; return true; } // Another implementation of the CNDF for a standard normal: N(0,1) double CumNorm(double x){ // protect against overflow if (x > 6.0) return 1.0; if (x < -6.0) return 0.0; double b1 = 0.31938153; double b2 = -0.356563782; double b3 = 1.781477937; double b4 = -1.821255978; double b5 = 1.330274429; double p = 0.2316419; double c2 = 0.3989423; double a = Math.abs(x); double t = 1.0 / (1.0 + a * p); double b = c2*Math.exp((-x)*(x/2.0)); double n = ((((b5*t+b4)*t+b3)*t+b2)*t+b1)*t; n = 1.0-b*n; if ( x < 0.0 ) n = 1.0 - n; return n; } public int run(PriceFeedData price){ if( price == null ) return 0; if( !initalized ){ type = -1; initalized = true; extreme = reference = price.elems.mid; return 0; } if( type == -1 ){ if( Math.log(price.elems.bid/extreme) >= deltaUp ){ type = 1; extreme = price.elems.ask; reference = price.elems.ask; return 1; } if( price.elems.ask < extreme ){ extreme = price.elems.ask; } if( Math.log(reference/extreme) >= dStar ){ reference = extreme; return 2; } } else if( type == 1 ){ if( Math.log(price.elems.ask/extreme) <= -deltaDown ){ type = -1; extreme = price.elems.bid; reference = price.elems.bid; return -1; } if( price.elems.bid > extreme ){ extreme = price.elems.bid; } if( Math.log(reference/extreme) <= -dStar ){ reference = extreme; return -2; } } return 0; } public boolean computation(PriceFeedData price){ if( price == null ) return false; int event = run(price); if( event != 0 ){ surp = alphaWeight*(Math.abs(event) == 1 ? 0.08338161 : 2.525729) + (1.0 - alphaWeight)*surp; if( event > 0 ){ // down moves downSurp = alphaWeight*(event == 1 ? 0.08338161 : 2.525729) + (1.0 - alphaWeight)*downSurp; }else if( event < 0 ){ // up moves upSurp = alphaWeight*(event == -1 ? 0.08338161 : 2.525729) + (1.0 - alphaWeight)*upSurp; } liq = 1.0 - CumNorm(Math.sqrt(alpha)*(surp - H1)/Math.sqrt(H2)); upLiq = 1.0 - CumNorm(Math.sqrt(alpha)*(upSurp - H1)/Math.sqrt(H2)); downLiq = 1.0 - CumNorm(Math.sqrt(alpha)*(downSurp - H1)/Math.sqrt(H2)); } return true; } }; public class CoastlineTrader{ double tP; /* -- Total position -- */ List<Double> prices; List<Double> sizes; double profitTarget; double pnl, tempPnl; double deltaUp, deltaDown, deltaLiq, deltaOriginal; double shrinkFlong, shrinkFshort; double pnlPerc; int longShort; boolean initalized; Runner runner; Runner[][] runnerG; double increaseLong, increaseShort; double lastPrice; double cashLimit; String fxRate; LocalLiquidity liquidity; CoastlineTrader(){}; CoastlineTrader(double dOriginal, double dUp, double dDown, double profitT, String FxRate, int lS){ prices = new LinkedList<Double>(); sizes = new LinkedList<Double>(); tP = 0.0; /* -- Total position -- */ profitTarget = cashLimit = profitT; pnl = tempPnl = pnlPerc = 0.0; deltaOriginal = dOriginal; deltaUp = dUp; deltaDown = dDown; longShort = lS; // 1 for only longs, -1 for only shorts shrinkFlong = shrinkFshort = 1.0; increaseLong = increaseShort = 0.0; fxRate = new String(FxRate); } double computePnl(PriceFeedData price){ // TODO: // Compute PnL with current price return 0.0; } double computePnlLastPrice(){ // TODO: // Compute PnL with last available price return 0.0; } double getPercPnl(PriceFeedData price){ // TODO: // Percentage PnL return 0.0; } boolean tryToClose(PriceFeedData price){ // TODO: // Check if PnL target hit implementation return false; } boolean assignCashTarget(){ // TODO: // Compute cash value corresponding to percentage PnL return true; } @SuppressWarnings("deprecation") boolean runPriceAsymm(PriceFeedData price, double oppositeInv){ if( !initalized ){ runner = new Runner(deltaUp, deltaDown, price, fxRate, deltaUp, deltaDown); runnerG = new Runner[2][2]; runnerG[0][0] = new Runner(0.75*deltaUp, 1.50*deltaDown, price, fxRate, 0.75*deltaUp, 0.75*deltaUp); runnerG[0][1] = new Runner(0.50*deltaUp, 2.00*deltaDown, price, fxRate, 0.50*deltaUp, 0.50*deltaUp); runnerG[1][0] = new Runner(1.50*deltaUp, 0.75*deltaDown, price, fxRate, 0.75*deltaDown, 0.75*deltaDown); runnerG[1][1] = new Runner(2.00*deltaUp, 0.50*deltaDown, price, fxRate, 0.50*deltaDown, 0.50*deltaDown); liquidity = new LocalLiquidity(deltaOriginal, deltaUp, deltaDown, price, deltaOriginal*2.525729, 50.0); initalized = true; } if( !liquidity.computation(price) ){ System.out.println("Didn't compute liquidity!"); } if( tryToClose(price) ){ /* -- Try to close position -- */ System.out.println("Close"); return true; } int event = 0; double fraction = 1.0; double size = (liquidity.liq < 0.5 ? 0.5 : 1.0); size = (liquidity.liq < 0.1 ? 0.1 : size); if( longShort == 1 ){ // Long positions only event = runner.run(price); if( 15.0 <= tP && tP < 30.0 ){ event = runnerG[0][0].run(price); runnerG[0][1].run(price); fraction = 0.5; }else if( tP >= 30.0 ){ event = runnerG[0][1].run(price); runnerG[0][0].run(price); fraction = 0.25; }else{ runnerG[0][0].run(price); runnerG[0][1].run(price); } if( event < 0 ){ if( tP == 0.0 ){ // Open long position int sign = -runner.type; if( Math.abs(oppositeInv) > 15.0 ){ size = 1.0; if( Math.abs(oppositeInv) > 30.0 ){ size = 1.0; } } double sizeToAdd = sign*size; tP += sizeToAdd; sizes.add(new Double(sizeToAdd)); prices.add(new Double(sign == 1 ? price.elems.ask : price.elems.bid)); assignCashTarget(); System.out.println("Open long"); } else if( tP > 0.0 ){ // Increase long position (buy) int sign = -runner.type; double sizeToAdd = sign*size*fraction*shrinkFlong; if( sizeToAdd < 0.0 ){ System.out.println("How did this happen! increase position but neg size: " + sizeToAdd); sizeToAdd = -sizeToAdd; } increaseLong += 1.0; tP += sizeToAdd; sizes.add(new Double(sizeToAdd)); prices.add(new Double(sign == 1 ? price.elems.ask : price.elems.bid)); System.out.println("Cascade"); } } else if( event > 0 && tP > 0.0 ){ // Possibility to decrease long position only at intrinsic events double pricE = (tP > 0.0 ? price.elems.bid : price.elems.ask); for( int i = 1; i < prices.size(); ++i ){ double tempP = (tP > 0.0 ? Math.log(pricE/prices.get(i).doubleValue()) : Math.log(prices.get(i).doubleValue()/pricE)); if( tempP >= (tP > 0.0 ? deltaUp : deltaDown) ){ double addPnl = (pricE - prices.get(i).doubleValue())*sizes.get(i).doubleValue(); if( addPnl < 0.0 ){ System.out.println("Descascade with a loss: " + addPnl); } tempPnl += addPnl; tP -= sizes.get(i).doubleValue(); sizes.remove(i); prices.remove(i); increaseLong += -1.0; System.out.println("Decascade"); } } } } else if( longShort == -1 ){ // Short positions only event = runner.run(price); if( -30.0 < tP && tP < -15.0 ){ event = runnerG[1][0].run(price); runnerG[1][1].run(price); fraction = 0.5; }else if( tP <= -30.0 ){ event = runnerG[1][1].run(price); runnerG[1][0].run(price); fraction = 0.25; }else{ runnerG[1][0].run(price); runnerG[1][1].run(price); } if( event > 0 ){ if( tP == 0.0 ){ // Open short position int sign = -runner.type; if( Math.abs(oppositeInv) > 15.0 ){ size = 1.0; if( Math.abs(oppositeInv) > 30.0 ){ size = 1.0; } } double sizeToAdd = sign*size; if( sizeToAdd > 0.0 ){ System.out.println("How did this happen! increase position but pos size: " + sizeToAdd); sizeToAdd = -sizeToAdd; } tP += sizeToAdd; sizes.add(new Double(sizeToAdd)); prices.add(new Double(sign == 1 ? price.elems.bid : price.elems.ask)); System.out.println("Open short"); assignCashTarget(); }else if( tP < 0.0 ){ int sign = -runner.type; double sizeToAdd = sign*size*fraction*shrinkFshort; if( sizeToAdd > 0.0 ){ System.out.println("How did this happen! increase position but pos size: " + sizeToAdd); sizeToAdd = -sizeToAdd; } tP += sizeToAdd; sizes.add(new Double(sizeToAdd)); increaseShort += 1.0; prices.add(new Double(sign == 1 ? price.elems.bid : price.elems.ask)); System.out.println("Cascade"); } } else if( event < 0.0 && tP < 0.0 ){ double pricE = (tP > 0.0 ? price.elems.bid : price.elems.ask); for( int i = 1; i < prices.size(); ++i ){ double tempP = (tP > 0.0 ? Math.log(pricE/prices.get(i).doubleValue()) : Math.log(prices.get(i).doubleValue()/pricE)); if( tempP >= (tP > 0.0 ? deltaUp : deltaDown) ){ double addPnl = (pricE - prices.get(i).doubleValue())*sizes.get(i).doubleValue(); if( addPnl < 0.0 ){ System.out.println("Descascade with a loss: " + addPnl); } tempPnl += (pricE - prices.get(i).doubleValue())*sizes.get(i).doubleValue(); tP -= sizes.get(i).doubleValue(); sizes.remove(i); prices.remove(i); increaseShort += -1.0; System.out.println("Decascade"); } } } } else{ System.out.println("Should never happen! " + longShort); } return true; } }; public class FXrateTrading{ CoastlineTrader[] coastTraderLong, coastTraderShort; String FXrate; Liquidity liquidity; double currentTime, oneDay; FXrateTrading(){}; FXrateTrading(String rate, int nbOfCoastTraders, double[] deltas){ currentTime = 1136073600000.0; oneDay = 24.0*60.0*60.0*1000.0; FXrate = new String(rate); coastTraderLong = new CoastlineTrader[nbOfCoastTraders]; coastTraderShort = new CoastlineTrader[nbOfCoastTraders]; for( int i = 0; i < coastTraderLong.length; ++i ){ coastTraderLong[i] = new CoastlineTrader(deltas[i], deltas[i], deltas[i], deltas[i], rate.toString(), 1); coastTraderShort[i] = new CoastlineTrader(deltas[i], deltas[i], deltas[i], deltas[i], rate.toString(), -1); } }; boolean runTradingAsymm(PriceFeedData price){ for( int i = 0; i < coastTraderLong.length; ++i ){ coastTraderLong[i].runPriceAsymm(price, coastTraderShort[i].tP); coastTraderShort[i].runPriceAsymm(price, coastTraderLong[i].tP); } if( price.elems.time >= currentTime + oneDay ){ while( currentTime <= price.elems.time ) currentTime += oneDay; printDataAsymm(currentTime); } return true; } boolean printDataAsymm(double time){ String sep = new String(System.getProperty("file.separator")); String folder = new String(sep + "home" + sep + "agolub" + sep + "workspace" + sep + "InvestmentStrategy" + sep + FXrate.toString() + "DataAsymmLiq.dat"); FileWriter fw = null; try{ double totalPos = 0.0, totalShort = 0.0, totalLong = 0.0; double totalPnl = 0.0; double totalPnlPerc = 0.0; fw = new FileWriter(folder, true); double price = -1.0; for( int i = 0; i < coastTraderLong.length; ++i ){ if( i == 0 ){ price = coastTraderLong[i].lastPrice; } totalLong += coastTraderLong[i].tP; totalShort += coastTraderShort[i].tP; totalPos += (coastTraderLong[i].tP + coastTraderShort[i].tP); totalPnl += (coastTraderLong[i].pnl + coastTraderLong[i].tempPnl + coastTraderLong[i].computePnlLastPrice() + coastTraderShort[i].pnl + coastTraderShort[i].tempPnl + coastTraderShort[i].computePnlLastPrice()); totalPnlPerc += (coastTraderLong[i].pnlPerc + (coastTraderLong[i].tempPnl + coastTraderLong[i].computePnlLastPrice())/coastTraderLong[i].cashLimit*coastTraderLong[i].profitTarget + coastTraderShort[i].pnlPerc + (coastTraderShort[i].tempPnl + coastTraderShort[i].computePnlLastPrice())/coastTraderShort[i].cashLimit*coastTraderShort[i].profitTarget); } fw.append((long)time + "," + totalPnl + "," + totalPnlPerc + "," + totalPos + "," + totalLong + "," + totalShort + "," + price + "\n"); fw.close(); } catch(IOException e){ System.out.println("Failed opening DC thresh file! " + e.getMessage()); return false; } return true; }; }; /* -- Price feed -- */ // Simple public class PriceFeedData{ // TODO Implement your feed Elems elems; double ask; PriceFeedData(){ elems = new Elems(); }; public class Elems{ double mid = 1.1; double ask = 1.1; double bid = 1.0; long time = System.currentTimeMillis(); } }; /* -- Configuration // TODO public static class Configuration{ public double startThreshold; public int numberOf; public double endThreshold; public Configuration() { startThreshold = 0.001; numberOf = 10; endThreshold = 0.05; } } */ } |
그리고 이상의 DC Intrinsic Time을 설명한 논문들이 다른 논문들과 비교하여 좋은 점은 관련한 논문에 사용한 방법을 Java 소스로 공개한 점입니다. Vladimir Petrov의 Risk management Tools based on Intrinsic Time and Scaling Laws을 보면 아래와 같이 공개하고 있습니다. 구글링을 해보면 DC Intrinsic Time을 주제로 한 트레이딩모델을 많이 볼 수 있습니다. ZeroAOS를 이용하여 구축해보면 어떨까 상상해봅니다.
The root directory contains several methods which can be directly employed for high-frequency real-time data analysis:
- LiquidityIndicator_Analysis.java – computes liquidity of historical time series represented by tick-by-tick price data using the multiscale interpretation of liquidity based on the directional change and scaling laws. It is a wrapper for the “LiquidityIndicator” class from the “ievent” folder. To run the program one needs to specify the path to the analysed file and set up several parameters described at the header of the code.
- InstantaneousVolatilityActivity_Analisys.java – this class is the extension of the “ievents/VolatilitySeasonality” class modifies to be applicable to the set of historical or real-time high-frequency price data.
- VolatilityEstimatorMovingWindow_Analysis.java – this class is designed to perform the volatility analysis of high-frequency data computed in two ways: using the sum of squared return function which calculates the aggregated standard deviation of the time series over fixed and equal intervals and using the method based on the directional-change intrinsic time approach in detail described in the RealizedVolatility.java class.
The ievents folder contains classes and methods related to the directional-change intrinsic time concept proposed and described in work “A geographical model for the daily and weekly seasonal volatility in the foreign exchange market” (available online):
- DcOS.java – Directional-Change dissection algorithm splits a historical price time series into a collection of directional-change and overshoot points defined as the sequence of alternating trend changes of selected size.
- IE.java – is an auxiliary class to keep information on the properties of each observed intrinsic event such as time and price levels.
- IntrinsicNetwork.java – is an example of the Intrinsic Network introduced in the “Multi-scale Representation of High Frequency Market Liquidity” paper (available online).
- LiquidityIndicator.java – is a realization of the liquidity indicator proposed in the research work “Multi-scale Representation of High Frequency Market Liquidity” (available online).
- InstantaneousVolatility.java – is the method used to translate the number of observed directional-change intrinsic events into the value of the instantaneous volatility. The approach is outlined in the research paper “Instantaneous Volatility Seasonality of Bitcoin in Directional-Change Intrinsic Time” (available online).
- MovingWindowVolatilityEstimator.java – is the instantaneous volatility estimator based on the number of Directional Changes observed over given period of time (set as a parameter of the method) and described in the paper “Instantaneous Volatility Seasonality of Bitcoin in Directional-Change Intrinsic Time” (available online).
- RealizedVolatility.java – is the class where the realized volatility is computed using the number of observed directional-change intrinsic events combined with the variability of overshoots proceeding each directional change. The main idea of the approach is in details described in the paper “Bridging the gap between physical and intrinsic time” (work in progress).
- RealizedVolatilitySeasonality.java – based on the RealizedVolatility.java method this class provides a novel way of computing weekly seasonality of the realized volatility considering the evolution of the price curve from the point of view of directional-change intrinsic events.
- InatantaneousVolatilitySeasonality.java – based on the InstantaneousVolatility.java method this class provides a novel way of computing weekly seasonality of the instantaneous volatility considering the evolution of the price curve from the point of view of directional-change intrinsic events.
- DcOSmultiD.java – is the extension of the originally one-dimensional version of the directional-change intrinsic time algorithm (class DcOS.java) to the multidimensional space. The space is formed by orthogonally placed independent exchange rates. The description of the multidimensional approach is provided in the paper “Multidimensional Directional-Change Intrinsic Time” (work in progress).
Folder scalinglaws contains tools which allow extracting scaling laws based on the directional-change intrinsic time as described in the paper “Patterns in high-frequency FX data: Discovery of 12 empirical scaling laws” (available online). Numbers of scaling laws follow the same order selected in the paper. The code has been validated using high-frequency data from Forex and Bitcoin markets (work in progress):
- MeanPriceMoveScalingLaw.java – “Mean price move scaling law”, Law 0a, p=1.
- QuadraticMeanPriceMoveScalingLaw.java – “Quadratic mean price move scaling law”, Law 0a, p=2.
- DCcountScalingLaw.java – “Number of Directional-Changes scaling law”, Law 0b.
- PriceMoveCountScalingLaw.java – “Price move count scaling law”, Law 2.
- MaxPriceMoveScalingLaw.java – “Maximal price move during scaling law”, Law 3, p=1.
- QuadraticMeanMaxPriceMoveScalingLaw.java – “Maximal price move during scaling law”, Law 3, p=2.
- MeanTimePriceMoveScalingLaw.java – “Mean time of price move scaling law”, Law 4.
- TimeDuringDCScalingLaw.java – “Time during directional-change scaling law”, Law 5.
- TotalMoveScalingLaw.java – “Total move scaling law”, Law 9(tm).
- OSmoveScalingLaw.java – “Overshoot scaling law”, Law 9(os).
- DCmoveScalingLaw.java – “Directional-change move scaling law”, Law 9(dc).
- TimeTotMoveScalLaw.java – “Time total-move scaling law”, Law 10(tm).
- TimeDCScalingLaw.java – “Time of directional change scaling law”, Law 10(dc).
- TimeOSScalingLaw.java – “Time of overshoot scaling law”, Law 10(os).
- TMtickCountScalingLaw.java – “Total move tick count scaling law”, Law 11(tm).
- DCtickCountScalingLaw.java – “Directional change tick count scaling law”, Law 11(dc).
- OStickCountScalingLaw.java – “Overshoot tick count scaling law”, Law 11(os).
- CumulTMScalingLaw.java – “Cumulative total move scaling law” (coastline), Law 12(tm).
- CumulOSScalingLaw.java – “Cumulative overshoot scaling law”, Law 12(os).
- CumulDCScalingLaw.java – “Cumulative directional change scaling law”, Law 12(dc).
Folder market contains a set of auxiliary tools nucessarily to perform the real application of risk management tools presented in the repository:
- Price.java – is a simple class for elementary prices corresponding to some given moment of time. These are tick prices by default. Each class instance holds information on the bid and ask price levels as well as the time of when the new tick was registered.
- SpreadInfo.java – this method, applied to a historical (or real-time streamed) time series, returns Median, Mean, Min and Max spread characteristics characterised some given period of time.
- PriceMultiD.java – is the class to keep information on the multidimensional structure of prices used for the multidimensional version of the directional-change intrinsic time algorithm. Collections of bid and ask prices composing each instance of the class are formed by individual components of exchange rates used in an experiment.
Folder tools is a collection of some traditional data management tools employed to analyse historical and real-time streams of high-frequency data:
- BM.java – is the class used to generate time series of given lengths properties of which coincide with properties of simple Brownian Motion process.
- GBM.java – is the class used to generate time series of given lengths following Geometrical Brownian Motion process.
- TraditionalVolatility.java – this class is traditional volatility estimator. Computes volatility of a given time series using the sum of squared returns.
- MovingWindowVolatilityEstimator.java – is the classical volatility estimator which performs over a moving window the length of which is defined as a parameter of the code. The algorithm is based on the sum of squared returns computed over equal time intervals.
- Tools.java – is a class containing a subset of simple methods used over the whole research work on the scaling laws and intrinsic time risk-management tools. The class is crucial for the stable work of almost all related experiments.
- ThetaTime.java – is the class the idea of which is to perform as theta-time presented in work of “A geographical model for the daily and weekly seasonal volatility in the foreign exchange market” (available online).
좋은 글 감사합니다
감사합니다…