Skip to content

Commit

Permalink
Merge pull request #301 from yulii/feature/fx-daily
Browse files Browse the repository at this point in the history
Feature fx daily
  • Loading branch information
yulii committed Dec 16, 2019
2 parents 62260ca + 6fa82c6 commit 155e0ef
Show file tree
Hide file tree
Showing 16 changed files with 916 additions and 32 deletions.
31 changes: 17 additions & 14 deletions lib/alpha_vantage.coffee
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
HttpQueryString = require './http/query_string'
AlphaVantageTimeSeriesDaily = require './alpha_vantage/time_series_daily'
AlphaVantageErrorMessage = require './alpha_vantage/error_message'
AlphaVantageSlackMessage = require './alpha_vantage/slack_message'
AlphaVantageFactory = require './alpha_vantage/factory'
AlphaVantageErrorMessage = require './alpha_vantage/error_message'
AlphaVantageSlackMessage = require './alpha_vantage/slack_message'

class AlphaVantage
_factory = undefined
_args = undefined
_endpoint = 'https://www.alphavantage.co/query'
_queryString = undefined

constructor: (args) ->
@function = args.function
@symbol = args.symbol
@token = process.env['ALPHA_VANTAGE_API_KEY']

endpoint: ->
return _endpoint
_args = args
_initialize.call @

execute: (robot, callback) ->
queryString = HttpQueryString.build(apikey: @token, function: @function, symbol: @symbol)

robot.http("#{@endpoint()}?#{queryString}")
robot.http("#{_endpoint}?#{_queryString()}")
.header('Content-Type', 'application/json')
.header('Accept', 'application/json')
.get() (error, response, body) ->
result = JSON.parse(body)
if result.hasOwnProperty('Error Message')
callback(new AlphaVantageErrorMessage(result))
else
outline = new AlphaVantageTimeSeriesDaily(result).outline()
callback(new AlphaVantageSlackMessage(outline))
callback(new AlphaVantageSlackMessage(_factory.parse(result).outline()))

_initialize = ->
_args.apikey = process.env['ALPHA_VANTAGE_API_KEY']
_factory = new AlphaVantageFactory(_args.function)

_queryString = ->
return HttpQueryString.build(_factory.query(_args).params())


module.exports = AlphaVantage
15 changes: 15 additions & 0 deletions lib/alpha_vantage/factory.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class AlphaVantageFactory
_name = undefined

constructor: (name) ->
_name = name.toLowerCase()

query: (params) ->
_query = require("./query/#{_name}")
return new _query(params)

parse: (object) ->
_parser = require("./parser/#{_name}")
return new _parser(object)

module.exports = AlphaVantageFactory
64 changes: 64 additions & 0 deletions lib/alpha_vantage/parser/fx_daily.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
moment = require('moment-timezone')

class AlphaVantageParserFxDaily
_meta = undefined
_data = undefined
_symbol = undefined
_timezone = undefined
_ts_keys = undefined

constructor: (object) ->
_meta = object['Meta Data']
_data = object['Time Series FX (Daily)']

_initialize.call @

outline: ->
limit = _ts_keys.length - 1
keys = [1, 20, 60, 180].filter((x) -> x < limit).map((x) -> _ts_keys[x])

compare = []
for t in keys
compare.push(
timestamp: t,
diff: @diffClosingPrice(t, @latestDate()),
ratio: @ratioClosingPrice(t, @latestDate())
)

return
timestamp: @latestDate()
symbol: @symbol()
price: @closingPrice(@latestDate())
compare: compare

symbol: ->
return "#{_symbol.from}/#{_symbol.to}"

timezone: ->
return _timezone

latestDate: ->
return _ts_keys[0]

closingPrice: (timestamp) ->
return parseFloat(_data[_key(timestamp)]['4. close'])

diffClosingPrice: (begin, end) ->
return _round(@closingPrice(end) - @closingPrice(begin), 5)

ratioClosingPrice: (begin, end) ->
return _round(100 * (@closingPrice(end) - @closingPrice(begin)) / @closingPrice(begin), 2)

_initialize = ->
_symbol = { from: _meta['2. From Symbol'], to: _meta['3. To Symbol'] }
_timezone = _meta['6. Time Zone']
_ts_keys = Object.keys(_data).map((x) -> moment.tz(x, _timezone).valueOf()).sort((a, b) -> return (a < b ? 1 : -1))

_key = (timestamp) ->
return moment(timestamp).tz(_timezone).format('YYYY-MM-DD')

_round = (value, digits) ->
n = Math.pow(10, digits)
return Math.round(value * n) / n

module.exports = AlphaVantageParserFxDaily
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
moment = require('moment-timezone')

class AlphaVantageTimeSeriesDaily
class AlphaVantageParserTimeSeriesDaily
_meta = undefined
_data = undefined
_symbol = undefined
Expand Down Expand Up @@ -61,4 +61,4 @@ class AlphaVantageTimeSeriesDaily
n = Math.pow(10, digits)
return Math.round(value * n) / n

module.exports = AlphaVantageTimeSeriesDaily
module.exports = AlphaVantageParserTimeSeriesDaily
27 changes: 27 additions & 0 deletions lib/alpha_vantage/query/fx_daily.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class AlphaVantageQueryFxDaily

constructor: (params) ->
@apikey = params.apikey
@function = params.function
@outputsize = params.outputsize ? 'compact'
@datatype = params.datatype ? 'json'
[@from_symbol, @to_symbol] = params.symbol.split('/')
_assert.call @

params: () ->
return {
apikey: @apikey
function: @function
from_symbol: @from_symbol
to_symbol: @to_symbol
outputsize: @outputsize
datatype: @datatype
}

_assert = ->
throw new Error('`apikey` is required argument') unless @apikey?
throw new Error('`function` is required argument') unless @function?
throw new Error('`from_symbol` is required argument') unless @from_symbol?
throw new Error('`to_symbol` is required argument') unless @to_symbol?

module.exports = AlphaVantageQueryFxDaily
25 changes: 25 additions & 0 deletions lib/alpha_vantage/query/time_series_daily.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class AlphaVantageQueryTimeSeriesDaily

constructor: (params) ->
@apikey = params.apikey
@function = params.function
@symbol = params.symbol
@outputsize = params.outputsize ? 'compact'
@datatype = params.datatype ? 'json'
_assert.call @

params: () ->
return {
apikey: @apikey
function: @function
symbol: @symbol
outputsize: @outputsize
datatype: @datatype
}

_assert = ->
throw new Error('`apikey` is required argument') unless @apikey?
throw new Error('`function` is required argument') unless @function?
throw new Error('`symbol` is required argument') unless @symbol?

module.exports = AlphaVantageQueryTimeSeriesDaily
12 changes: 12 additions & 0 deletions scripts/alpha_vantage.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,15 @@ module.exports = (robot) ->
)
catch error
msg.send "#{error.name}: #{error.message}"

robot.respond /fx ([a-z0-9^]+\/[a-z0-9^]+)$/i, (msg) ->
try
new AlphaVantage(
function: 'FX_DAILY',
symbol: msg.match[1]
).execute(robot, (message) ->
robot.logger.debug(message.toString())
msg.send(message.format())
)
catch error
msg.send "#{error.name}: #{error.message}"
10 changes: 10 additions & 0 deletions scripts/webhook.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ module.exports = (robot) ->
catch error
robot.send { room: '#devops' }, "#{error.name}: #{error.message}"

['USD/JPY'].forEach (name) ->
try
new AlphaVantage(
function: 'FX_DAILY', symbol: name
).execute(robot, (message) ->
robot.send { room: '#random' }, message.format()
)
catch error
robot.send { room: '#devops' }, "#{error.name}: #{error.message}"

when /wake up!/i.test(req.body.text)
robot.send { room: '#general' }, "I'm readly for @#{req.body.user_name}"

Expand Down
Loading

0 comments on commit 155e0ef

Please sign in to comment.