1
+ # === Setting ======================================================
2
+ library(' shiny' )
3
+ library(' memoise' )
4
+ library(' magrittr' )
5
+ library(' stringr' )
6
+ library(' TFX' )
7
+ library(' quantmod' )
8
+ library(' rugarch' )
9
+ library(' lubridate' )
10
+ library(' formattable' )
11
+
12
+ # === Data =====================================================
13
+ Sys.setenv(TZ = ' Asia/Tokyo' )
14
+ zones <- attr(as.POSIXlt(Sys.time()), ' tzone' )
15
+ zone <- ifelse(zones [[1 ]] == ' ' , paste(zones [- 1 ], collapse = ' /' ), zones [[1 ]])
16
+
17
+ # === Function =====================================================
18
+ # ARMA Modeling寻找AIC值最小的p,q
19
+ armaSearch <- suppressWarnings(function (data , .method = ' CSS-ML' ){
20
+ # # I set .method = 'CSS-ML' as default method since the AIC value we got is
21
+ # # smaller than using method 'ML' while using method 'CSS' facing error.
22
+ # #
23
+ # # https://stats.stackexchange.com/questions/209730/fitting-methods-in-arima
24
+ # # According to the documentation, this is how each method fits the model:
25
+ # # - CSS minimises the sum of squared residuals.
26
+ # # - ML maximises the log-likelihood function of the ARIMA model.
27
+ # # - CSS-ML mixes both methods: first, CSS is run, the starting parameters
28
+ # # for the optimization algorithm are set to zeros or to the values given
29
+ # # in the optional argument init; then, ML is applied passing the CSS
30
+ # # parameter estimates as starting parameter values for the optimization algorithm.
31
+
32
+ .methods = c(' CSS-ML' , ' ML' , ' CSS' )
33
+
34
+ if (! .method %in% .methods ) stop(paste(' Kindly choose .method among ' ,
35
+ paste0(.methods , collapse = ' , ' ), ' !' ))
36
+
37
+ armacoef <- data.frame ()
38
+ for (p in 0 : 5 ){
39
+ for (q in 0 : 5 ) {
40
+ # data.arma = arima(diff(data), order = c(p, 0, q))
41
+ # '@ data.arma = arima(data, order = c(p, 1, q), method = .method)
42
+ if (.method == ' CSS-ML' ) {
43
+ data.arma = tryCatch({
44
+ arma = arima(data , order = c(p , 1 , q ), method = ' CSS-ML' )
45
+ mth = ' CSS-ML'
46
+ list (arma , mth )
47
+ }, error = function (e ) {
48
+ arma = arima(data , order = c(p , 1 , q ), method = ' ML' )
49
+ mth = ' ML'
50
+ list (arma = arma , mth = mth )
51
+ })
52
+ } else if (.method == ' ML' ) {
53
+ data.arma = tryCatch({
54
+ arma = arima(data , order = c(p , 1 , q ), method = ' ML' )
55
+ mth = ' ML'
56
+ list (arma = arma , mth = mth )
57
+ }, error = function (e ) {
58
+ arma = arima(data , order = c(p , 1 , q ), method = ' CSS-ML' )
59
+ mth = ' CSS-ML'
60
+ list (arma = arma , mth = mth )
61
+ })
62
+ } else if (.method == ' CSS' ) {
63
+ data.arma = tryCatch({
64
+ arma = arima(data , order = c(p , 1 , q ), method = ' CSS' )
65
+ mth = ' CSS'
66
+ list (arma = arma , mth = mth )
67
+ }, error = function (e ) {
68
+ arma = arima(data , order = c(p , 1 , q ), method = ' CSS-ML' )
69
+ mth = ' CSS-ML'
70
+ list (arma = arma , mth = mth )
71
+ })
72
+ } else {
73
+ stop(paste(' Kindly choose .method among ' , paste0(.methods , collapse = ' , ' ), ' !' ))
74
+ }
75
+ names(data.arma ) <- c(' arma' , ' mth' )
76
+
77
+ # cat('p =', p, ', q =', q, 'AIC =', data.arma$arma$aic, '\n')
78
+ armacoef <- rbind(armacoef , c(p , q , data.arma $ arma $ aic ))
79
+ }
80
+ }
81
+
82
+ colnames(armacoef ) <- c(' p' , ' q' , ' AIC' )
83
+ pos <- which(armacoef $ AIC == min(armacoef $ AIC ))
84
+ cat(paste0(' method = \' ' , data.arma $ mth , ' \' , the min AIC = ' , armacoef $ AIC [pos ],
85
+ ' , p = ' , armacoef $ p [pos ], ' , q = ' , armacoef $ q [pos ], ' \n ' ))
86
+ return (armacoef )
87
+ })
88
+
89
+ getFOREX <- memoise(function (currency ) {
90
+ getSymbols(currency , from = today(' Asia/Tokyo' ) %m - % years(1 ), to = today(' Asia/Tokyo' ))
91
+ if (currency == ' AUD=X' ) {
92
+ mbase <- `AUD=X` %> % Cl %> % na.omit ; rm(`AUD=X` )
93
+ mbase <- 1 / mbase
94
+ names(mbase ) %<> % str_replace_all(' AUD=X.Close' , ' AUD.USD' )
95
+
96
+ } else if (currency == ' EUR=X' ) {
97
+ mbase <- `EUR=X` %> % Cl %> % na.omit ; rm(`EUR=X` )
98
+ mbase <- 1 / mbase
99
+ names(mbase ) %<> % str_replace_all(' EUR=X.Close' , ' EUR.USD' )
100
+
101
+ } else if (currency == ' GBP=X' ) {
102
+ mbase <- `GBP=X` %> % Cl %> % na.omit ; rm(`GBP=X` )
103
+ mbase <- 1 / mbase
104
+ names(mbase ) %<> % str_replace_all(' GBP=X.Close' , ' GBP.USD' )
105
+
106
+ } else if (currency == ' CHF=X' ) {
107
+ mbase <- `CHF=X` %> % Cl %> % na.omit ; rm(`CHF=X` )
108
+ names(mbase ) %<> % str_replace_all(' CHF=X.Close' , ' USD.CHF' )
109
+
110
+ } else if (currency == ' CAD=X' ) {
111
+ mbase <- `CAD=X` %> % Cl %> % na.omit ; rm(`CAD=X` )
112
+ names(mbase ) %<> % str_replace_all(' CAD=X.Close' , ' USD.CAD' )
113
+
114
+ } else if (currency == ' CNY=X' ) {
115
+ mbase <- `CNY=X` %> % Cl %> % na.omit ; rm(`CNY=X` )
116
+ names(mbase ) %<> % str_replace_all(' CNY=X.Close' , ' USD.CNY' )
117
+
118
+ } else if (currency == ' JPY=X' ) {
119
+ mbase <- `JPY=X` %> % Cl %> % na.omit ; rm(`JPY=X` )
120
+ names(mbase ) %<> % str_replace_all(' JPY=X.Close' , ' USD.JPY' )
121
+
122
+ } else {
123
+ stop(' Kindly choose common currencies exchange.' )
124
+ }
125
+ return (mbase )
126
+ })
127
+
128
+ # Using "memoise" to automatically cache the results
129
+ calC <- memoise(function (currency , ahead ) {
130
+
131
+ mbase = getFOREX(currency )
132
+ armaOrder = armaSearch(mbase )
133
+ armaOrder %<> % dplyr :: filter(AIC == min(AIC )) %> % . [c(' p' , ' q' )] %> % unlist
134
+
135
+ spec = ugarchspec(
136
+ variance.model = list (
137
+ model = ' gjrGARCH' , garchOrder = c(1 , 1 ),
138
+ submodel = NULL , external.regressors = NULL ,
139
+ variance.targeting = FALSE ),
140
+ mean.model = list (
141
+ armaOrder = armaOrder ,
142
+ include.mean = TRUE , archm = FALSE ,
143
+ archpow = 1 , arfima = FALSE ,
144
+ external.regressors = NULL ,
145
+ archex = FALSE ),
146
+ distribution.model = ' snorm' )
147
+ fit = ugarchfit(spec , mbase , solver = ' hybrid' )
148
+ fc = ugarchforecast(fit , n.ahead = ahead )
149
+ res = attributes(fc )$ forecast $ seriesFor
150
+ colnames(res ) = names(mbase )
151
+
152
+ sim = ugarchsim(fit , n.sim = 1000 , m.sim = 25 , rseed = 1 : 25 )
153
+
154
+ tmp = list (latestPrice = tail(mbase , 1 ), forecastPrice = res ,
155
+ sim = sim )
156
+ return (tmp )
157
+ })
158
+
159
+
160
+ # === Shiny UI =====================================================
161
+ ui <- shinyUI(fluidPage(
162
+
163
+ titlePanel(
164
+ tags $ a(href = ' http://www.binary.com' , target = ' _blank' ,
165
+ tags $ img(height = ' 80px' , alt = ' binary' , # align='right',
166
+ src = ' https://raw.githubusercontent.com/englianhu/binary.com-interview-question/master/www/binary-logo-resize.jpg' ))),
167
+
168
+ sidebarLayout(
169
+ sidebarPanel(
170
+ selectInput(' curr' , ' Currency :' ,
171
+ choices = c(' AUD/USD' = ' AUD=X' ,
172
+ ' EUR/USD' = ' EUR=X' ,
173
+ ' GBP/USD' = ' GBP=X' ,
174
+ ' USD/CHF' = ' CHF=X' ,
175
+ ' USD/CAD' = ' CAD=X' ,
176
+ ' USD/CNY' = ' CNY=X' ,
177
+ ' USD/JPY' = ' JPY=X' ),
178
+ selected = ' USD/JPY' ),
179
+ sliderInput(' ahead' , HTML(' Forecast ahead (ζ in day) :' ),
180
+ min = 1 , max = 7 , step = 1 , value = 7 )),
181
+
182
+ mainPanel(
183
+ tabsetPanel(
184
+ tabPanel(' Board' ,
185
+ h3(' Real Time Price' ),
186
+ div(class = ' container' ,
187
+ p(strong(paste0(' Current time (' , zone , ' ):' )),
188
+ textOutput(' currentTime' )
189
+ ),
190
+ p(strong(' Latest FX Quotes:' ),
191
+ tableOutput(' fxdata' ),
192
+ checkboxInput(' pause' , ' Pause updates' , FALSE ))
193
+ )),
194
+ tabPanel(' Trade' ,
195
+ h3(' Latest Price' ),
196
+ htmlOutput(' lastPr' ),
197
+ br(),
198
+ p(' You can either buy or sell at the mentioned price.' ),
199
+ formattableOutput(' fcastPr' )),
200
+ tabPanel(' Forecast' ,
201
+ h3(' Forecast Trend' ),
202
+ plotOutput(' sim' )),
203
+ tabPanel(' Appendix' ,
204
+ tabsetPanel(
205
+ tabPanel(' Reference' ,
206
+ h3(' Future Works' ),
207
+ p(' For the API and also real-time data visualization, I put it as future research...' ,
208
+ tags $ ul(
209
+ tags $ li(HTML(" <a href='http://www.techrepublic.com/blog/five-apps/create-real-time-graphs-with-these-five-free-web-based-apps/'>Create real-time graphs with these five free web-based apps</a>" )),
210
+ tags $ li(HTML(" <a href='https://www.slideshare.net/rorywinston/streaming-data-in-r'>Streaming Data in R</a>" )),
211
+ tags $ li(HTML(" <a href='https://www.quora.com/How-I-can-manage-real-time-data-with-R-programming-and-Tableau'>How I can manage real time data with R programming and Tableau?</a>" )),
212
+ tags $ li(HTML(" <a href='https://www.r-bloggers.com/real-time-predictive-analytics-with-big-data-and-r/'>Real-Time Predictive Analytics with Big Data, and R</a>" )),
213
+ tags $ li(HTML(" <a href='https://stackoverflow.com/questions/37049634/streaming-data-visualization-in-r'>Streaming data visualization in R</a>" )))),
214
+ br(),
215
+ h3(' Reference' ),
216
+ p(' 01. ' , HTML(" <a href='https://www.r-bloggers.com/accessing-apis-from-r-and-a-little-r-programming/'>Accessing APIs from R (and a little R programming)</a>" )),
217
+ p(' 02. ' , HTML(" <a href='https://developers.binary.com/'>Welcome to the Binary.com API</a>" )),
218
+ p(' 03. ' , HTML(" <a href='https://stats.stackexchange.com/questions/6021/r-update-a-graph-dynamically?answertab=votes#tab-top'>R: update a graph dynamically</a>" )),
219
+ p(' 04. ' , HTML(" <a href='https://www.r-bloggers.com/tfx-package/'>TFX Package</a>" )),
220
+ p(' 05. ' , HTML(" <a href='http://rpubs.com/gsee/TFX'>TFX: An R Interface to the TrueFX™ Web API</a>" ),
221
+ tags $ a(href = ' https://github.com/scibrokes/owner' , target = ' _blank' ,
222
+ tags $ img(height = ' 20px' , alt = ' hot' , # align='right',
223
+ src = ' https://raw.githubusercontent.com/englianhu/binary.com-interview-question/master/www/hot.jpg' ))),
224
+ p(' 06. ' , HTML(" <a href='https://gist.github.com/gsee/4122626'>shiny TrueFX quotes</a>" ),
225
+ tags $ a(href = ' https://github.com/scibrokes/owner' , target = ' _blank' ,
226
+ tags $ img(height = ' 20px' , alt = ' hot' , # align='right',
227
+ src = ' https://raw.githubusercontent.com/englianhu/binary.com-interview-question/master/www/hot.jpg' )))),
228
+
229
+ tabPanel(' Author' ,
230
+ h3(' Author' ),
231
+ tags $ iframe(src = ' https://englianhu.github.io/2016/12/ryo-eng.html' , height = 800 , width = ' 100%' , frameborder = 0 ))))))),
232
+ br(),
233
+ p(' Powered by - Copyright® Intellectual Property Rights of ' ,
234
+ tags $ a(href = ' http://www.scibrokes.com' , target = ' _blank' ,
235
+ tags $ img(height = ' 20px' , alt = ' scibrokes' , # align='right',
236
+ src = ' https://raw.githubusercontent.com/scibrokes/betting-strategy-and-model-validation/master/regressionApps/oda-army.jpg' )),
237
+ HTML(" <a href='http://www.scibrokes.com'>Scibrokes®</a>" ))))
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+ # === Shiny Server ===============================================
246
+ # Based on (more) code by Joe Cheng:
247
+ # https://groups.google.com/d/msg/shiny-discuss/NE-LqDAVqQQ/kNdrtC4WxGAJ
248
+ # https://gist.github.com/4044364
249
+ # -------------------------------------------------------------------------------
250
+ server <- shinyServer(function (input , output , session ) {
251
+
252
+ output $ currentTime <- renderText({
253
+ # Forces invalidation in 1000 milliseconds
254
+ invalidateLater(1000 , session )
255
+ as.character(Sys.time())
256
+ })
257
+
258
+ fetchData <- reactive({
259
+ if (! input $ pause )
260
+ invalidateLater(750 )
261
+ qtf <- QueryTrueFX()
262
+ qtf $ TimeStamp <- as.character(qtf $ TimeStamp )
263
+ names(qtf )[6 ] <- ' TimeStamp (GMT)'
264
+ qtf [, c(6 , 1 : 3 , 5 : 4 )]
265
+ })
266
+
267
+ output $ fxdata <- renderTable({
268
+ fetchData()
269
+ }, digits = 5 , row.names = FALSE )
270
+
271
+
272
+ terms <- reactive({
273
+ # # Change when the "update" button is pressed...
274
+ input $ curr
275
+ input $ ahead
276
+ # # ...but not for anything else
277
+ isolate({
278
+ withProgress({
279
+ setProgress(message = " Processing algorithmic forecast..." )
280
+ calC(input $ curr , input $ ahead )
281
+ })
282
+ })
283
+ })
284
+
285
+ output $ lastPr <- renderText({
286
+ tmp = terms()$ latestPrice
287
+ paste(' The latest closing price of' , ' <font color=\" #FF0000\" ><b>' ,
288
+ names(tmp ), ' </b></font>' , ' on <font color=\" #FF0000\" ><b>' ,
289
+ index(tmp ), ' </b></font>' , ' is' , ' <font color=\" #FF0000\" ><b>' ,
290
+ tmp , ' </b></font>' )
291
+ })
292
+
293
+ output $ fcastPr <- renderFormattable({
294
+ tmp = terms()
295
+ data.frame (Buy = ' BUY' , Price = tmp $ forecastPrice , Sell = ' SELL' ) %> %
296
+ formattable(list (
297
+ Buy = formatter(' span' , style = ~ style(color = ifelse(
298
+ Buy == ' SELL' , ' red' , ' green' )),
299
+ ~ icontext(ifelse(Buy == ' SELL' , ' arrow-down' , ' arrow-up' ), Buy )),
300
+ Price = color_tile(' white' , ' darkgolden' ),
301
+ Sell = formatter(' span' , style = ~ style(color = ifelse(
302
+ Sell == ' SELL' , ' red' , ' green' )),
303
+ ~ icontext(ifelse(Sell == ' SELL' , ' arrow-down' , ' arrow-up' ), Sell ))))
304
+ })
305
+
306
+ output $ sim <- renderPlot({
307
+ tmp = terms()$ sim
308
+ plot(tmp , which = ' all' , m.sim = 24 )
309
+ })
310
+
311
+
312
+ })
313
+
314
+ # Run the application
315
+ shinyApp(ui = ui , server = server )
316
+ # '@ shiny::runApp('Q2', display.mode = 'showcase')
0 commit comments