packages feed

oanda-rest-api 0.1.0.0 → 0.3.0.0

raw patch · 17 files changed

+564/−473 lines, 17 filesdep +http-conduitdep +oanda-rest-apidep +thymedep −timedep −wreqdep ~basedep ~hlintdep ~hspecPVP ok

version bump matches the API change (PVP)

Dependencies added: http-conduit, oanda-rest-api, thyme

Dependencies removed: time, wreq

Dependency ranges changed: base, hlint, hspec

API changes (from Hackage documentation)

- OANDA.Accounts: instance Data.Aeson.Types.Class.FromJSON OANDA.Accounts.Account
- OANDA.Accounts: instance Data.Aeson.Types.Class.FromJSON OANDA.Accounts.AccountInfo
- OANDA.Accounts: instance GHC.Generics.Constructor OANDA.Accounts.C1_0Account
- OANDA.Accounts: instance GHC.Generics.Constructor OANDA.Accounts.C1_0AccountInfo
- OANDA.Accounts: instance GHC.Generics.Datatype OANDA.Accounts.D1Account
- OANDA.Accounts: instance GHC.Generics.Datatype OANDA.Accounts.D1AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_0Account
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_0AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_10AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_1Account
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_1AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_2Account
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_2AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_3Account
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_3AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_4AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_5AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_6AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_7AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_8AccountInfo
- OANDA.Accounts: instance GHC.Generics.Selector OANDA.Accounts.S1_0_9AccountInfo
- OANDA.Orders: instance Data.Aeson.Types.Class.FromJSON OANDA.Orders.Order
- OANDA.Orders: instance GHC.Generics.Constructor OANDA.Orders.C1_0Order
- OANDA.Orders: instance GHC.Generics.Datatype OANDA.Orders.D1Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_0Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_10Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_11Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_12Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_1Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_2Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_3Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_4Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_5Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_6Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_7Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_8Order
- OANDA.Orders: instance GHC.Generics.Selector OANDA.Orders.S1_0_9Order
- OANDA.Positions: instance Data.Aeson.Types.Class.FromJSON OANDA.Positions.CloseResponse
- OANDA.Positions: instance Data.Aeson.Types.Class.FromJSON OANDA.Positions.Position
- OANDA.Positions: instance GHC.Generics.Constructor OANDA.Positions.C1_0CloseResponse
- OANDA.Positions: instance GHC.Generics.Constructor OANDA.Positions.C1_0Position
- OANDA.Positions: instance GHC.Generics.Datatype OANDA.Positions.D1CloseResponse
- OANDA.Positions: instance GHC.Generics.Datatype OANDA.Positions.D1Position
- OANDA.Positions: instance GHC.Generics.Selector OANDA.Positions.S1_0_0CloseResponse
- OANDA.Positions: instance GHC.Generics.Selector OANDA.Positions.S1_0_0Position
- OANDA.Positions: instance GHC.Generics.Selector OANDA.Positions.S1_0_1CloseResponse
- OANDA.Positions: instance GHC.Generics.Selector OANDA.Positions.S1_0_1Position
- OANDA.Positions: instance GHC.Generics.Selector OANDA.Positions.S1_0_2CloseResponse
- OANDA.Positions: instance GHC.Generics.Selector OANDA.Positions.S1_0_2Position
- OANDA.Positions: instance GHC.Generics.Selector OANDA.Positions.S1_0_3CloseResponse
- OANDA.Positions: instance GHC.Generics.Selector OANDA.Positions.S1_0_3Position
- OANDA.Rates: instance Data.Aeson.Types.Class.FromJSON OANDA.Rates.BidAskCandlesResponse
- OANDA.Rates: instance Data.Aeson.Types.Class.FromJSON OANDA.Rates.BidAskCandlestick
- OANDA.Rates: instance Data.Aeson.Types.Class.FromJSON OANDA.Rates.Instrument
- OANDA.Rates: instance Data.Aeson.Types.Class.FromJSON OANDA.Rates.MidpointCandlesResponse
- OANDA.Rates: instance Data.Aeson.Types.Class.FromJSON OANDA.Rates.MidpointCandlestick
- OANDA.Rates: instance Data.Aeson.Types.Class.FromJSON OANDA.Rates.Price
- OANDA.Rates: instance GHC.Generics.Constructor OANDA.Rates.C1_0BidAskCandlesResponse
- OANDA.Rates: instance GHC.Generics.Constructor OANDA.Rates.C1_0BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Constructor OANDA.Rates.C1_0Instrument
- OANDA.Rates: instance GHC.Generics.Constructor OANDA.Rates.C1_0MidpointCandlesResponse
- OANDA.Rates: instance GHC.Generics.Constructor OANDA.Rates.C1_0MidpointCandlestick
- OANDA.Rates: instance GHC.Generics.Constructor OANDA.Rates.C1_0Price
- OANDA.Rates: instance GHC.Generics.Datatype OANDA.Rates.D1BidAskCandlesResponse
- OANDA.Rates: instance GHC.Generics.Datatype OANDA.Rates.D1BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Datatype OANDA.Rates.D1Instrument
- OANDA.Rates: instance GHC.Generics.Datatype OANDA.Rates.D1MidpointCandlesResponse
- OANDA.Rates: instance GHC.Generics.Datatype OANDA.Rates.D1MidpointCandlestick
- OANDA.Rates: instance GHC.Generics.Datatype OANDA.Rates.D1Price
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_0BidAskCandlesResponse
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_0BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_0Instrument
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_0MidpointCandlesResponse
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_0MidpointCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_0Price
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_10BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_1BidAskCandlesResponse
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_1BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_1Instrument
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_1MidpointCandlesResponse
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_1MidpointCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_1Price
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_2BidAskCandlesResponse
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_2BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_2Instrument
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_2MidpointCandlesResponse
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_2MidpointCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_2Price
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_3BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_3Instrument
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_3MidpointCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_3Price
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_4BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_4Instrument
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_4MidpointCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_5BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_5Instrument
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_5MidpointCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_6BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_6Instrument
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_6MidpointCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_7BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_7Instrument
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_8BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_8Instrument
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_9BidAskCandlestick
- OANDA.Rates: instance GHC.Generics.Selector OANDA.Rates.S1_0_9Instrument
- OANDA.Trades: instance Data.Aeson.Types.Class.FromJSON OANDA.Trades.Trade
- OANDA.Trades: instance GHC.Generics.Constructor OANDA.Trades.C1_0Trade
- OANDA.Trades: instance GHC.Generics.Datatype OANDA.Trades.D1Trade
- OANDA.Trades: instance GHC.Generics.Selector OANDA.Trades.S1_0_0Trade
- OANDA.Trades: instance GHC.Generics.Selector OANDA.Trades.S1_0_1Trade
- OANDA.Trades: instance GHC.Generics.Selector OANDA.Trades.S1_0_2Trade
- OANDA.Trades: instance GHC.Generics.Selector OANDA.Trades.S1_0_3Trade
- OANDA.Trades: instance GHC.Generics.Selector OANDA.Trades.S1_0_4Trade
- OANDA.Trades: instance GHC.Generics.Selector OANDA.Trades.S1_0_5Trade
- OANDA.Trades: instance GHC.Generics.Selector OANDA.Trades.S1_0_6Trade
- OANDA.Trades: instance GHC.Generics.Selector OANDA.Trades.S1_0_7Trade
- OANDA.Trades: instance GHC.Generics.Selector OANDA.Trades.S1_0_8Trade
- OANDA.Trades: instance GHC.Generics.Selector OANDA.Trades.S1_0_9Trade
- OANDA.Transactions: instance Data.Aeson.Types.Class.FromJSON OANDA.Transactions.Transaction
- OANDA.Transactions: instance GHC.Generics.Constructor OANDA.Transactions.C1_0Transaction
- OANDA.Transactions: instance GHC.Generics.Datatype OANDA.Transactions.D1Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_0Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_10Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_11Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_12Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_13Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_14Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_15Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_1Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_2Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_3Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_4Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_5Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_6Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_7Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_8Transaction
- OANDA.Transactions: instance GHC.Generics.Selector OANDA.Transactions.S1_0_9Transaction
- OANDA.Types: AccessToken :: ByteString -> AccessToken
- OANDA.Types: AccountID :: Int -> AccountID
- OANDA.Types: Buy :: Side
- OANDA.Types: Live :: APIType
- OANDA.Types: Practice :: APIType
- OANDA.Types: Sandbox :: APIType
- OANDA.Types: Sell :: Side
- OANDA.Types: [unAccessToken] :: AccessToken -> ByteString
- OANDA.Types: [unAccountID] :: AccountID -> Int
- OANDA.Types: accessToken :: OandaEnv -> Maybe AccessToken
- OANDA.Types: apiEndpoint :: APIType -> String
- OANDA.Types: apiType :: OandaEnv -> APIType
- OANDA.Types: data APIType
- OANDA.Types: data OandaEnv
- OANDA.Types: data Side
- OANDA.Types: instance Data.Aeson.Types.Class.FromJSON OANDA.Types.Side
- OANDA.Types: instance GHC.Generics.Constructor OANDA.Types.C1_0Side
- OANDA.Types: instance GHC.Generics.Constructor OANDA.Types.C1_1Side
- OANDA.Types: instance GHC.Generics.Datatype OANDA.Types.D1Side
- OANDA.Types: instance GHC.Generics.Generic OANDA.Types.Side
- OANDA.Types: instance GHC.Show.Show OANDA.Types.APIType
- OANDA.Types: instance GHC.Show.Show OANDA.Types.AccessToken
- OANDA.Types: instance GHC.Show.Show OANDA.Types.AccountID
- OANDA.Types: instance GHC.Show.Show OANDA.Types.OandaEnv
- OANDA.Types: instance GHC.Show.Show OANDA.Types.Side
- OANDA.Types: liveAuth :: AccessToken -> OandaEnv
- OANDA.Types: newtype AccessToken
- OANDA.Types: newtype AccountID
- OANDA.Types: practiceAuth :: AccessToken -> OandaEnv
- OANDA.Types: sandboxAuth :: OandaEnv
- OANDA.Types: type InstrumentText = Text
+ OANDA.Accounts: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Accounts.Account
+ OANDA.Accounts: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Accounts.AccountInfo
+ OANDA.Environment: AccessToken :: ByteString -> AccessToken
+ OANDA.Environment: AccountID :: Int -> AccountID
+ OANDA.Environment: Buy :: Side
+ OANDA.Environment: Live :: APIType
+ OANDA.Environment: Practice :: APIType
+ OANDA.Environment: Sandbox :: APIType
+ OANDA.Environment: Sell :: Side
+ OANDA.Environment: [unAccessToken] :: AccessToken -> ByteString
+ OANDA.Environment: [unAccountID] :: AccountID -> Int
+ OANDA.Environment: accessToken :: OandaEnv -> Maybe AccessToken
+ OANDA.Environment: apiType :: OandaEnv -> APIType
+ OANDA.Environment: data APIType
+ OANDA.Environment: data OandaEnv
+ OANDA.Environment: data Side
+ OANDA.Environment: liveAuth :: AccessToken -> OandaEnv
+ OANDA.Environment: newtype AccessToken
+ OANDA.Environment: newtype AccountID
+ OANDA.Environment: practiceAuth :: AccessToken -> OandaEnv
+ OANDA.Environment: sandboxAuth :: OandaEnv
+ OANDA.Environment: type InstrumentText = Text
+ OANDA.Orders: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Orders.Order
+ OANDA.Positions: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Positions.CloseResponse
+ OANDA.Positions: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Positions.Position
+ OANDA.Rates: EndCount :: ZonedTime -> Int -> CandlesCount
+ OANDA.Rates: StartCount :: ZonedTime -> Int -> Bool -> CandlesCount
+ OANDA.Rates: [count] :: CandlesCount -> Int
+ OANDA.Rates: granularityToDiffTime :: Granularity -> NominalDiffTime
+ OANDA.Rates: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Rates.BidAskCandlesResponse
+ OANDA.Rates: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Rates.BidAskCandlestick
+ OANDA.Rates: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Rates.Instrument
+ OANDA.Rates: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Rates.MidpointCandlesResponse
+ OANDA.Rates: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Rates.MidpointCandlestick
+ OANDA.Rates: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Rates.Price
+ OANDA.Trades: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Trades.Trade
+ OANDA.Transactions: instance Data.Aeson.Types.FromJSON.FromJSON OANDA.Transactions.Transaction
- OANDA.Accounts: Account :: Int -> String -> String -> Decimal -> Account
+ OANDA.Accounts: Account :: Int -> Text -> Text -> Decimal -> Account
- OANDA.Accounts: AccountInfo :: Integer -> String -> Decimal -> Decimal -> Decimal -> Decimal -> Decimal -> Integer -> Integer -> Decimal -> String -> AccountInfo
+ OANDA.Accounts: AccountInfo :: Integer -> Text -> Decimal -> Decimal -> Decimal -> Decimal -> Decimal -> Integer -> Integer -> Decimal -> Text -> AccountInfo
- OANDA.Accounts: [accountAccountCurrency] :: Account -> String
+ OANDA.Accounts: [accountAccountCurrency] :: Account -> Text
- OANDA.Accounts: [accountAccountName] :: Account -> String
+ OANDA.Accounts: [accountAccountName] :: Account -> Text
- OANDA.Accounts: [accountInfoAccountCurrency] :: AccountInfo -> String
+ OANDA.Accounts: [accountInfoAccountCurrency] :: AccountInfo -> Text
- OANDA.Accounts: [accountInfoAccountName] :: AccountInfo -> String
+ OANDA.Accounts: [accountInfoAccountName] :: AccountInfo -> Text
- OANDA.Orders: Order :: Integer -> InstrumentText -> Integer -> Side -> String -> ZonedTime -> Decimal -> Decimal -> Decimal -> ZonedTime -> Decimal -> Decimal -> Decimal -> Order
+ OANDA.Orders: Order :: Integer -> InstrumentText -> Integer -> Side -> Text -> ZonedTime -> Decimal -> Decimal -> Decimal -> ZonedTime -> Decimal -> Decimal -> Decimal -> Order
- OANDA.Orders: [orderType] :: Order -> String
+ OANDA.Orders: [orderType] :: Order -> Text
- OANDA.Positions: Position :: String -> Int -> Side -> Decimal -> Position
+ OANDA.Positions: Position :: Text -> Int -> Side -> Decimal -> Position
- OANDA.Positions: [positionInstrument] :: Position -> String
+ OANDA.Positions: [positionInstrument] :: Position -> Text
- OANDA.Rates: CandlesArgs :: CandlesCount -> Granularity -> Int -> String -> DayOfWeek -> CandlesArgs
+ OANDA.Rates: CandlesArgs :: Maybe CandlesCount -> Maybe Granularity -> Maybe Int -> Maybe Text -> Maybe DayOfWeek -> CandlesArgs
- OANDA.Rates: Instrument :: String -> Maybe Decimal -> Maybe Integer -> Maybe String -> Maybe Decimal -> Maybe Decimal -> Maybe Decimal -> Maybe Decimal -> Maybe String -> Maybe Decimal -> Instrument
+ OANDA.Rates: Instrument :: Text -> Maybe Decimal -> Maybe Integer -> Maybe Text -> Maybe Decimal -> Maybe Decimal -> Maybe Decimal -> Maybe Decimal -> Maybe Bool -> Maybe Object -> Instrument
- OANDA.Rates: InstrumentsArgs :: [Text] -> [Text] -> InstrumentsArgs
+ OANDA.Rates: InstrumentsArgs :: Maybe [Text] -> Maybe [Text] -> InstrumentsArgs
- OANDA.Rates: [candlesAlignmentTZ] :: CandlesArgs -> String
+ OANDA.Rates: [candlesAlignmentTZ] :: CandlesArgs -> Maybe Text
- OANDA.Rates: [candlesCount] :: CandlesArgs -> CandlesCount
+ OANDA.Rates: [candlesCount] :: CandlesArgs -> Maybe CandlesCount
- OANDA.Rates: [candlesDailyAlignment] :: CandlesArgs -> Int
+ OANDA.Rates: [candlesDailyAlignment] :: CandlesArgs -> Maybe Int
- OANDA.Rates: [candlesGranularity] :: CandlesArgs -> Granularity
+ OANDA.Rates: [candlesGranularity] :: CandlesArgs -> Maybe Granularity
- OANDA.Rates: [candlesWeeklyAlignment] :: CandlesArgs -> DayOfWeek
+ OANDA.Rates: [candlesWeeklyAlignment] :: CandlesArgs -> Maybe DayOfWeek
- OANDA.Rates: [instrumentDisplayName] :: Instrument -> Maybe String
+ OANDA.Rates: [instrumentDisplayName] :: Instrument -> Maybe Text
- OANDA.Rates: [instrumentHalted] :: Instrument -> Maybe String
+ OANDA.Rates: [instrumentHalted] :: Instrument -> Maybe Bool
- OANDA.Rates: [instrumentInstrument] :: Instrument -> String
+ OANDA.Rates: [instrumentInstrument] :: Instrument -> Text
- OANDA.Rates: [instrumentInterestRate] :: Instrument -> Maybe Decimal
+ OANDA.Rates: [instrumentInterestRate] :: Instrument -> Maybe Object
- OANDA.Rates: [instrumentsFields] :: InstrumentsArgs -> [Text]
+ OANDA.Rates: [instrumentsFields] :: InstrumentsArgs -> Maybe [Text]
- OANDA.Rates: [instrumentsInstruments] :: InstrumentsArgs -> [Text]
+ OANDA.Rates: [instrumentsInstruments] :: InstrumentsArgs -> Maybe [Text]
- OANDA.Trades: Trade :: !Int -> !Int -> !Side -> !String -> !ZonedTime -> !Decimal -> !Decimal -> !Decimal -> !Decimal -> !Decimal -> Trade
+ OANDA.Trades: Trade :: !Int -> !Int -> !Side -> !Text -> !ZonedTime -> !Decimal -> !Decimal -> !Decimal -> !Decimal -> !Decimal -> Trade
- OANDA.Trades: [tradeInstrument] :: Trade -> !String
+ OANDA.Trades: [tradeInstrument] :: Trade -> !Text

Files

CHANGES.md view
@@ -1,3 +1,24 @@-# 0.1.0.0+# Changelog++## 0.3.0.0++- Prefer `Text` instead of `String` for arguments. We still use `String` in+  endpoints since that is what `wreq` uses.+- Use `http-conduit` instead of `wreq`++## 0.2.0.0++- Use thyme instead of time. Thyme uses a much more efficient representation of+  time stamps. Note that thyme has a module called Data.Thyme.Time that+  provides wrappers and conversion functions to and from time types.+- Added a convenient `granularityToDiffTime` to convert from `Granularity` to+  `NominalDiffTime`.+- Fixed not being able to use a start/end time in conjunction with a count for+  the candlestick endpoints.+- Use true optional arguments using `Maybe`. This fixes some endpoints breaking+  when empty lists were passed, and also makes it so we don't have to hard-code+  defaults.++## 0.1.0.0  Initial release. The API is not yet complete, but there is enough to be useful.
oanda-rest-api.cabal view
@@ -1,81 +1,124 @@-name:                oanda-rest-api-version:             0.1.0.0-synopsis:            Client to the OANDA REST API-description:         Client to the OANDA REST API-homepage:            http://github.com/jdreaver/oanda-rest-api-license:             BSD3-license-file:        LICENSE-author:              John David Reaver-maintainer:          johndreaver@gmail.com-copyright:           (c) 2015-2016 John David Reaver-category:            API-build-type:          Simple-stability:           experimental-extra-source-files:  README.md-                   , CHANGES.md-cabal-version:       >=1.10--library-  hs-source-dirs:      src-  ghc-options:         -Wall-  exposed-modules:     OANDA-                     , OANDA.Accounts-                     , OANDA.Orders-                     , OANDA.Positions-                     , OANDA.Rates-                     , OANDA.Trades-                     , OANDA.Transactions-                     , OANDA.Types-  other-modules:       OANDA.Util-  build-depends:       base >= 4.7 && < 5-                     , aeson >= 0.8.0-                     , bytestring >= 0.10.0-                     , containers >= 0.5.2-                     , Decimal >= 0.4.2-                     , lens >= 4.0-                     , old-locale >= 1.0.0.6-                     , scientific >= 0.3.3.0-                     , text >= 1.2.0-                     , time >= 1.4-                     , vector >= 0.10.0-                     , wreq >= 0.3.0.0-  default-language:    Haskell2010+-- This file has been generated from package.yaml by hpack version 0.15.0.+--+-- see: https://github.com/sol/hpack +name:           oanda-rest-api+version:        0.3.0.0+synopsis:       Client to the OANDA REST API+description:    Client to the OANDA REST API+category:       API+stability:      experimental+homepage:       https://github.com/jdreaver/oanda-rest-api#readme+bug-reports:    https://github.com/jdreaver/oanda-rest-api/issues+author:         John David Reaver+maintainer:     johndreaver@gmail.com+copyright:      (c) 2015-2016 John David Reaver+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10 -test-suite tests-    type:             exitcode-stdio-1.0-    main-is:          Spec.hs-    build-depends:    base >=4 && <5-                    , aeson >= 0.8.0-                    , bytestring >= 0.10.0-                    , containers >= 0.5.2-                    , Decimal >= 0.4.2-                    , lens >= 4.0-                    , old-locale >= 1.0.0.6-                    , scientific >= 0.3.3.0-                    , text >= 1.2.0-                    , time >= 1.4-                    , vector >= 0.10.0-                    , wreq >= 0.3.0.0+extra-source-files:+    CHANGES.md+    README.md+    stack.yaml -                    , hspec ==2.*-                    , HUnit -any-    default-language: Haskell2010-    hs-source-dirs:   src tests-    ghc-prof-options: -prof -fprof-auto-    ghc-options:      -Wall+source-repository head+  type: git+  location: https://github.com/jdreaver/oanda-rest-api +library+  hs-source-dirs:+      src+  ghc-options: -Wall+  build-depends:+      base >= 4.7 && < 5+    , aeson >= 0.8.0+    , bytestring >= 0.10.0+    , containers >= 0.5.2+    , Decimal >= 0.4.2+    , http-conduit+    , lens >= 4.0+    , old-locale >= 1.0.0.6+    , scientific >= 0.3.3.0+    , text >= 1.2.0+    , thyme+    , vector >= 0.10.0+  exposed-modules:+      OANDA+      OANDA.Accounts+      OANDA.Environment+      OANDA.Orders+      OANDA.Positions+      OANDA.Rates+      OANDA.Trades+      OANDA.Transactions+  other-modules:+      OANDA.Internal+      OANDA.Internal.Import+      OANDA.Internal.Request+      OANDA.Internal.Types+  default-language: Haskell2010  test-suite style-    type:             exitcode-stdio-1.0-    main-is:          HLint.hs-    build-depends:    base -any-                    , hlint ==1.*-    default-language: Haskell2010-    hs-source-dirs:   tests-    ghc-options:      -Wall-+  type: exitcode-stdio-1.0+  main-is: HLint.hs+  hs-source-dirs:+      tests+  ghc-options: -Wall+  build-depends:+      base >= 4.7 && < 5+    , aeson >= 0.8.0+    , bytestring >= 0.10.0+    , containers >= 0.5.2+    , Decimal >= 0.4.2+    , http-conduit+    , lens >= 4.0+    , old-locale >= 1.0.0.6+    , scientific >= 0.3.3.0+    , text >= 1.2.0+    , thyme+    , vector >= 0.10.0+    , hlint+  other-modules:+      Spec+  default-language: Haskell2010 -source-repository head-  type:     git-  location: https://github.com/jdreaver/oanda-rest-api+test-suite tests+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  hs-source-dirs:+      src+    , tests+  ghc-options: -Wall+  build-depends:+      base >= 4.7 && < 5+    , aeson >= 0.8.0+    , bytestring >= 0.10.0+    , containers >= 0.5.2+    , Decimal >= 0.4.2+    , http-conduit+    , lens >= 4.0+    , old-locale >= 1.0.0.6+    , scientific >= 0.3.3.0+    , text >= 1.2.0+    , thyme+    , vector >= 0.10.0+    , oanda-rest-api+    , hspec+    , HUnit+  other-modules:+      OANDA+      OANDA.Accounts+      OANDA.Environment+      OANDA.Internal+      OANDA.Internal.Import+      OANDA.Internal.Request+      OANDA.Internal.Types+      OANDA.Orders+      OANDA.Positions+      OANDA.Rates+      OANDA.Trades+      OANDA.Transactions+      HLint+  default-language: Haskell2010
src/OANDA.hs view
@@ -1,19 +1,11 @@ module OANDA-    ( module OANDA.Rates-    , module OANDA.Accounts-    , module OANDA.Orders-    , module OANDA.Positions-    , module OANDA.Trades-    , module OANDA.Transactions-    , module OANDA.Types+    ( module X     ) where -import OANDA.Accounts-import OANDA.Orders-import OANDA.Positions-import OANDA.Rates-import OANDA.Trades-import OANDA.Transactions-import OANDA.Types--{-# ANN module "HLint: ignore Use import/export shortcut" #-}+import OANDA.Accounts as X+import OANDA.Environment as X+import OANDA.Orders as X+import OANDA.Positions as X+import OANDA.Rates as X+import OANDA.Trades as X+import OANDA.Transactions as X
src/OANDA/Accounts.hs view
@@ -6,26 +6,21 @@ -- API.  module OANDA.Accounts-       ( Account (..)-       , accounts-       , AccountInfo (..)-       , accountInfo-       ) where+  ( Account (..)+  , accounts+  , AccountInfo (..)+  , accountInfo+  ) where -import           Data.Aeson-import           Data.Decimal import qualified Data.Vector as V-import           GHC.Generics (Generic) -import           OANDA.Util-import           OANDA.Types-+import OANDA.Internal  -- | Wraps the JSON response for accounts data Account = Account   { accountAccountId       :: Int-  , accountAccountName     :: String-  , accountAccountCurrency :: String+  , accountAccountName     :: Text+  , accountAccountCurrency :: Text   , accountMarginRate      :: Decimal   } deriving (Show, Generic) @@ -35,22 +30,22 @@ -- | Get all accounts for given access token accounts :: OandaEnv -> IO (V.Vector Account) accounts od = do-  let url = baseURL od ++ "/v1/accounts"-      opts = constructOpts od []-  jsonResponseArray url opts "accounts"+  let url = "GET " ++ baseURL od ++ "/v1/accounts"+  request <- constructRequest od url []+  jsonResponseArray request "accounts"   -- | Get all account info associated with an account ID. accountInfo :: OandaEnv -> AccountID -> IO AccountInfo-accountInfo od (AccountID aid) =-  do let url = baseURL od ++ "/v1/accounts/" ++ show aid-         opts = constructOpts od []-     jsonResponse url opts+accountInfo od (AccountID aid) = do+  let url = "GET " ++ baseURL od ++ "/v1/accounts/" ++ show aid+  request <- constructRequest od url []+  jsonResponse request   data AccountInfo = AccountInfo   { accountInfoAccountId       :: Integer-  , accountInfoAccountName     :: String+  , accountInfoAccountName     :: Text   , accountInfoBalance         :: Decimal   , accountInfoUnrealizedPl    :: Decimal   , accountInfoRealizedPl      :: Decimal@@ -59,7 +54,7 @@   , accountInfoOpenTrades      :: Integer   , accountInfoOpenOrders      :: Integer   , accountInfoMarginRate      :: Decimal-  , accountInfoAccountCurrency :: String+  , accountInfoAccountCurrency :: Text   } deriving (Show, Generic)  instance FromJSON AccountInfo where
+ src/OANDA/Environment.hs view
@@ -0,0 +1,17 @@+-- | Definition of types used to set up the API.++module OANDA.Environment+  ( OandaEnv+  , apiType+  , accessToken+  , sandboxAuth+  , practiceAuth+  , liveAuth+  , APIType (..)+  , AccessToken (..)+  , AccountID (..)+  , Side (..)+  , InstrumentText+  ) where++import OANDA.Internal
+ src/OANDA/Internal.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- | Utility functions.++module OANDA.Internal+       ( module X+       ) where++import OANDA.Internal.Import as X+import OANDA.Internal.Request as X+import OANDA.Internal.Types as X
+ src/OANDA/Internal/Import.hs view
@@ -0,0 +1,35 @@+-- | Common imports used throughout the library. Easier to just put them in one+-- spot.++module OANDA.Internal.Import+  ( module X+  , (.~)+  , (^.)+  , (&)+  , mzero+  , toLower+  , catMaybes+  , Text+  , intercalate+  , unpack+  , pack+  , Generic+  , defaultTimeLocale+  , (<>)+  ) where++import Control.Lens ((.~), (^.), (&))+import Control.Monad (mzero)+import Data.Aeson as X+import Data.Char (toLower)+import Data.Decimal as X+import Data.Maybe (catMaybes)+import Data.Monoid ((<>))+import Data.Scientific as X+import Data.Text (Text, intercalate, unpack, pack)+import Data.Text.Encoding as X+import Data.Thyme as X+import Data.Thyme.Format.Aeson as X ()+import GHC.Generics (Generic)+import Network.HTTP.Simple as X+import System.Locale (defaultTimeLocale)
+ src/OANDA/Internal/Request.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE OverloadedStrings #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++-- | Internal module for dealing with requests via wreq++module OANDA.Internal.Request+  ( constructRequest+  , baseURL+  , commaList+  , jsonOpts+  , jsonResponse+  , jsonResponseArray+  -- , jsonDelete+  , formatTimeRFC3339+  ) where++import qualified Data.Aeson.TH as TH+import qualified Data.ByteString as BS+import qualified Data.Map as Map++import OANDA.Internal.Import+import OANDA.Internal.Types++-- | Specifies the endpoints for each `APIType`. These are the base URLs for+-- each API call.+baseURL :: OandaEnv -> String+baseURL env = apiEndpoint (apiType env)+  where apiEndpoint Sandbox  = "http://api-sandbox.oanda.com"+        apiEndpoint Practice = "https://api-fxpractice.oanda.com"+        apiEndpoint Live     = "https://api-fxtrade.oanda.com"++constructRequest :: OandaEnv -> String -> [(Text, Maybe [Text])] -> IO Request+constructRequest env url params = do+  initRequest <- parseRequest url+  return $+    initRequest+    & maybe id makeAuthHeader (accessToken env)+    & setRequestQueryString params'+  where+    makeAuthHeader (AccessToken t) = addRequestHeader "Authorization" ("Bearer " `BS.append` t)+    paramToMaybe (name, Just xs') = Just (encodeUtf8 name, Just $ encodeUtf8 $ commaList xs')+    paramToMaybe (_, Nothing) = Nothing+    params' = catMaybes $ fmap paramToMaybe params++-- | Convert a Maybe [Text] item into empty text or comma-separated text.+commaList :: [Text] -> Text+commaList = intercalate ","++-- | Used to derive FromJSON instances.+jsonOpts :: String -> TH.Options+jsonOpts s = TH.defaultOptions { TH.fieldLabelModifier = firstLower . drop (length s) }+  where firstLower [] = []+        firstLower (x:xs) = toLower x : xs++-- | Boilerplate function to perform a request and extract the response body.+jsonResponse :: (FromJSON a) => Request -> IO a+jsonResponse request = getResponseBody <$> httpJSON request++-- | Boilerplate function to perform a request and extract the response body.+jsonResponseArray :: (FromJSON a) => Request -> String -> IO a+jsonResponseArray request name =+  do body <- jsonResponse request+     return $ body Map.! (name :: String)++-- | Formats time according to RFC3339 (which is the time format used by+-- OANDA). Taken from the <https://github.com/HugoDaniel/timerep timerep> library.+formatTimeRFC3339 :: ZonedTime -> String+formatTimeRFC3339 zt@(ZonedTime _ z) = formatTime defaultTimeLocale "%FT%T" zt <> printZone+  where timeZoneStr = timeZoneOffsetString z+        printZone = if timeZoneStr == timeZoneOffsetString utc+                    then "Z"+                    else take 3 timeZoneStr <> ":" <> drop 3 timeZoneStr+++instance (Integral a) => FromJSON (DecimalRaw a) where+  parseJSON (Number n) = readDecimalJSON n+  parseJSON (String s) = readDecimalJSON (read (unpack s))+  parseJSON _          = mempty+++readDecimalJSON :: (Num i, Applicative f) => Scientific -> f (DecimalRaw i)+readDecimalJSON n = pure $ Decimal ((*) (-1) $ fromIntegral $ base10Exponent n)+                                    (fromIntegral $ coefficient n)
+ src/OANDA/Internal/Types.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}++-- | Defines types used in the REST API++module OANDA.Internal.Types+       ( OandaEnv+       , apiType+       , accessToken+       , sandboxAuth+       , practiceAuth+       , liveAuth+       , APIType (..)+       , AccessToken (..)+       , AccountID (..)+       , Side (..)+       , InstrumentText+       ) where++import qualified Data.ByteString as BS+import OANDA.Internal.Import++-- | Wraps an `APIType` and an `AccessToken`. Mainly just a convenience wrapper+-- to make functions have fewer arguments. To instantiate this type, use the+-- `sandboxAuth`, `practiceAuth`, or `liveAuth` functions.+data OandaEnv = OandaEnv+  { apiType     :: APIType+  , accessToken :: Maybe AccessToken+  } deriving (Show)++-- | Use the sandbox API.+sandboxAuth :: OandaEnv+sandboxAuth = OandaEnv Sandbox Nothing++-- | Use the practice API.+practiceAuth :: AccessToken -> OandaEnv+practiceAuth = OandaEnv Practice . Just++-- | Use the live API.+liveAuth :: AccessToken -> OandaEnv+liveAuth = OandaEnv Live . Just+++-- | The three endpoint types used in the REST API. See the following link for+-- details: <http://developer.oanda.com/rest-live/development-guide/>+data APIType = Sandbox+             | Practice+             | Live+             deriving (Show)++-- | The token given by OANDA used to access the API+newtype AccessToken = AccessToken { unAccessToken :: BS.ByteString }+                      deriving (Show)+++-- | Integer representing the Account ID of an account+newtype AccountID = AccountID { unAccountID :: Int}+                    deriving (Show)++-- | Used when reporting a position in the API+data Side = Buy+          | Sell+          deriving (Show, Generic)++instance FromJSON Side where+  parseJSON (String s) = fmap readSide (pure $ unpack s)+  parseJSON _          = mzero+++readSide :: String -> Side+readSide "buy"  = Buy+readSide "sell" = Sell+readSide _      = error "No parse Side"++type InstrumentText = Text
src/OANDA/Orders.hs view
@@ -5,27 +5,20 @@   module OANDA.Orders-       ( openOrders-       , Order (..)-       ) where-+  ( openOrders+  , Order (..)+  ) where -import           Data.Aeson-import           Data.Decimal-import           Data.Time (ZonedTime) import qualified Data.Vector as V-import           GHC.Generics (Generic) -import           OANDA.Types-import           OANDA.Util-+import OANDA.Internal  -- | Get all open orders for an account. openOrders :: OandaEnv -> AccountID -> IO (V.Vector Order)-openOrders od (AccountID aid) =-  do let url = baseURL od ++ "/v1/accounts/" ++ show aid ++ "/orders"-         opts = constructOpts od []-     jsonResponseArray url opts "orders"+openOrders od (AccountID aid) = do+  let url = "GET " ++ baseURL od ++ "/v1/accounts/" ++ show aid ++ "/orders"+  request <- constructRequest od url []+  jsonResponseArray request "orders"   data Order = Order@@ -33,7 +26,7 @@   , orderInstrument   :: InstrumentText   , orderUnits        :: Integer   , orderSide         :: Side-  , orderType         :: String -- "marketIfTouched",+  , orderType         :: Text -- "marketIfTouched",   , orderTime         :: ZonedTime   , orderPrice        :: Decimal   , orderTakeProfit   :: Decimal
src/OANDA/Positions.hs view
@@ -6,31 +6,26 @@ -- API.  module OANDA.Positions-       ( Position (..)-       , openPositions-       , position-       , closePosition-       , CloseResponse (..)-       ) where+  ( Position (..)+  , openPositions+  , position+  , closePosition+  , CloseResponse (..)+  ) where -import           Data.Aeson-import           Data.Decimal-import           Data.Text (unpack) import qualified Data.Vector as V-import           GHC.Generics (Generic) -import           OANDA.Util-import           OANDA.Types+import OANDA.Internal  -- | Get all open positions for an account. openPositions :: OandaEnv -> AccountID -> IO (V.Vector Position)-openPositions od (AccountID aid) =-  do let url = baseURL od ++ "/v1/accounts/" ++ show aid ++ "/positions"-         opts = constructOpts od []-     jsonResponseArray url opts "positions"+openPositions od (AccountID aid) = do+  let url = "GET " ++ baseURL od ++ "/v1/accounts/" ++ show aid ++ "/positions"+  request <- constructRequest od url []+  jsonResponseArray request "positions"  data Position = Position-  { positionInstrument :: String+  { positionInstrument :: Text   , positionUnits      :: Int   , positionSide       :: Side   , positionAvgPrice   :: Decimal@@ -39,22 +34,19 @@ instance FromJSON Position where   parseJSON = genericParseJSON $ jsonOpts "position" - -- | Get open position for an account on a given instrument. position :: OandaEnv -> AccountID -> InstrumentText -> IO Position-position od (AccountID aid) ins =-  do let url = baseURL od ++ "/v1/accounts/" ++ show aid ++ "/positions/" ++ unpack ins-         opts = constructOpts od []-     jsonResponse url opts-+position od (AccountID aid) ins = do+  let url = "GET " ++ baseURL od ++ "/v1/accounts/" ++ show aid ++ "/positions/" ++ unpack ins+  request <- constructRequest od url []+  jsonResponse request  -- | Closes an existing position. closePosition :: OandaEnv -> AccountID -> InstrumentText -> IO CloseResponse-closePosition od (AccountID aid) ins =-  do let url = baseURL od ++ "/v1/accounts/" ++ show aid ++ "/positions/" ++ unpack ins-         opts = constructOpts od []-     jsonDelete url opts-+closePosition od (AccountID aid) ins = do+  let url = "DELETE " ++ baseURL od ++ "/v1/accounts/" ++ show aid ++ "/positions/" ++ unpack ins+  request <- constructRequest od url []+  jsonResponse request  data CloseResponse = CloseResponse   { closeResponseIds        :: V.Vector Int@@ -62,7 +54,6 @@   , closeResponseTotalUnits :: Int   , closeResponsePrice      :: Decimal   } deriving (Show, Generic)-  instance FromJSON CloseResponse where   parseJSON = genericParseJSON $ jsonOpts "closeResponse"
src/OANDA/Rates.hs view
@@ -5,55 +5,48 @@ -- <http://developer.oanda.com/rest-live/rates/ Rates> section of the API.  module OANDA.Rates-       ( InstrumentsArgs (..)-       , instrumentsArgs-       , Instrument (..)-       , instruments-       , Price (..)-       , prices-       , MidpointCandlestick (..)-       , midpointCandles-       , BidAskCandlestick (..)-       , bidaskCandles-       , CandlesArgs (..)-       , candlesArgs-       , CandlesCount (..)-       , DayOfWeek (..)-       , Granularity (..)-       ) where+  ( InstrumentsArgs (..)+  , instrumentsArgs+  , Instrument (..)+  , instruments+  , Price (..)+  , prices+  , MidpointCandlestick (..)+  , midpointCandles+  , BidAskCandlestick (..)+  , bidaskCandles+  , CandlesArgs (..)+  , candlesArgs+  , CandlesCount (..)+  , DayOfWeek (..)+  , Granularity (..)+  , granularityToDiffTime+  ) where -import           Data.Aeson-import           Data.Char (toLower)-import           Data.Decimal-import           Data.Text (Text, pack)-import           Data.Time import qualified Data.Vector as V-import           GHC.Generics (Generic)-import           Network.Wreq (Options) -import           OANDA.Util-import           OANDA.Types+import OANDA.Internal   data InstrumentsArgs = InstrumentsArgs-  { instrumentsFields      :: [Text]-  , instrumentsInstruments :: [Text]+  { instrumentsFields      :: Maybe [Text]+  , instrumentsInstruments :: Maybe [Text]   } deriving (Show)  instrumentsArgs :: InstrumentsArgs-instrumentsArgs = InstrumentsArgs ["displayName", "pip", "maxTradeUnits"] []+instrumentsArgs = InstrumentsArgs Nothing Nothing  data Instrument = Instrument-  { instrumentInstrument      :: String+  { instrumentInstrument      :: Text   , instrumentPip             :: Maybe Decimal   , instrumentMaxTradeUnits   :: Maybe Integer-  , instrumentDisplayName     :: Maybe String+  , instrumentDisplayName     :: Maybe Text   , instrumentPrecision       :: Maybe Decimal   , instrumentMaxTrailingStop :: Maybe Decimal   , instrumentMinTrailingStop :: Maybe Decimal   , instrumentMarginRate      :: Maybe Decimal-  , instrumentHalted          :: Maybe String-  , instrumentInterestRate    :: Maybe Decimal+  , instrumentHalted          :: Maybe Bool+  , instrumentInterestRate    :: Maybe Object   } deriving (Show, Generic)  instance FromJSON Instrument where@@ -62,22 +55,22 @@ -- | Retrieve a list of instruments from OANDA instruments :: OandaEnv -> AccountID -> InstrumentsArgs -> IO (V.Vector Instrument) instruments od (AccountID aid) (InstrumentsArgs fs is) = do-  let url = baseURL od ++ "/v1/instruments"-      opts = constructOpts od [ ("accountId", [pack $ show aid])-                              , ("instruments", is)-                              , ("fields", fs)-                              ]-  jsonResponseArray url opts "instruments"-+  let url = "GET " ++ baseURL od ++ "/v1/instruments"+  request <- constructRequest od url+    [ ("accountId", Just [pack $ show aid])+    , ("instruments", is)+    , ("fields", fs)+    ]+  jsonResponseArray request "instruments"  -- | Retrieve the current prices for a list of instruments. prices :: OandaEnv -> [InstrumentText] -> Maybe ZonedTime -> IO (V.Vector Price)-prices od is zt =-  do let url = baseURL od ++ "/v1/prices"-         ztOpt = maybe [] (\zt' -> [("since", [pack $ formatTimeRFC3339 zt'])]) zt-         opts = constructOpts od $ ("instruments", is) : ztOpt--     jsonResponseArray url opts "prices"+prices od is zt = do+  let+    url = "GET " ++ baseURL od ++ "/v1/prices"+    ztOpt = maybe [] (\zt' -> [("since", Just [pack $ formatTimeRFC3339 zt'])]) zt+  request <- constructRequest od url (("instruments", Just is) : ztOpt)+  jsonResponseArray request "prices"  data Price = Price   { priceInstrument :: InstrumentText@@ -93,36 +86,47 @@ -- | Retrieve the price history of a single instrument in midpoint candles midpointCandles :: OandaEnv -> InstrumentText -> CandlesArgs ->                    IO (V.Vector MidpointCandlestick)-midpointCandles od i args =-  do let (url, opts) = candleOpts od i args "midpoint"-     response <- jsonResponse url opts :: IO MidpointCandlesResponse-     return $ _midcandlesResponseCandles response+midpointCandles od i args = do+  request <- candleOpts od i args "midpoint"+  response <- jsonResponse request :: IO MidpointCandlesResponse+  return $ _midcandlesResponseCandles response  -- | Retrieve the price history of a single instrument in bid/ask candles bidaskCandles :: OandaEnv -> InstrumentText -> CandlesArgs ->                  IO (V.Vector BidAskCandlestick)-bidaskCandles od i args =-  do let (url, opts) = candleOpts od i args "bidask"-     response <- jsonResponse url opts :: IO BidAskCandlesResponse-     return $ _bidaskResponseCandles response+bidaskCandles od i args = do+  request <- candleOpts od i args "bidask"+  response <- jsonResponse request :: IO BidAskCandlesResponse+  return $ _bidaskResponseCandles response   -- | Utility function for both candle history functions-candleOpts :: OandaEnv -> InstrumentText -> CandlesArgs -> String -> (String, Options)-candleOpts od i (CandlesArgs c g di atz wa) fmt = (url, opts)-  where url   = baseURL od ++ "/v1/candles"-        opts  = constructOpts od $ [ ("instrument", [i])-                                   , ("granularity", [pack $ show g])-                                   , ("candleFormat", [pack fmt])-                                   , ("dailyAlignment", [pack $ show di])-                                   , ("alignmentTimeZone", [pack atz])-                                   , ("weeklyAlignment", [pack $ show wa])-                                   ] ++ countOpts c-        countOpts (Count count) = [("count", [pack $ show count])]-        countOpts (StartEnd st ed incf) = [ ("start", [pack $ formatTimeRFC3339 st])-                                          , ("end", [pack $ formatTimeRFC3339 ed])-                                          , ("includeFirst", [pack $ map toLower (show incf)])-                                          ]+candleOpts :: OandaEnv -> InstrumentText -> CandlesArgs -> String -> IO Request+candleOpts od i (CandlesArgs c g di atz wa) fmt = constructRequest od url opts+  where url = "GET " ++ baseURL od ++ "/v1/candles"+        opts =+          [ ("instrument", Just [i])+          , ("granularity", (:[]) . pack . show <$> g)+          , ("candleFormat", Just [pack fmt])+          , ("dailyAlignment", (:[]) . pack . show <$> di)+          , ("alignmentTimeZone", (:[]) <$> atz)+          , ("weeklyAlignment", (:[]) . pack . show <$> wa)+          ] ++ maybe [] countOpts c+        countOpts (Count c') = [("count", Just [pack $ show c'])]+        countOpts (StartEnd st ed incf) =+          [ ("start", Just [pack $ formatTimeRFC3339 st])+          , ("end", Just [pack $ formatTimeRFC3339 ed])+          , ("includeFirst", Just [pack $ map toLower (show incf)])+          ]+        countOpts (StartCount st c' incf) =+          [ ("start", Just [pack $ formatTimeRFC3339 st])+          , ("count", Just [pack $ show c'])+          , ("includeFirst", Just [pack $ map toLower (show incf)])+          ]+        countOpts (EndCount ed c') =+          [ ("end", Just [pack $ formatTimeRFC3339 ed])+          , ("count", Just [pack $ show c'])+          ]  data MidpointCandlestick = MidpointCandlestick   { midpointCandlestickTime     :: ZonedTime@@ -156,21 +160,31 @@   parseJSON = genericParseJSON $ jsonOpts "bidaskCandlestick"  data CandlesArgs = CandlesArgs-  { candlesCount           :: CandlesCount-  , candlesGranularity     :: Granularity-  , candlesDailyAlignment  :: Int-  , candlesAlignmentTZ     :: String-  , candlesWeeklyAlignment :: DayOfWeek+  { candlesCount           :: Maybe CandlesCount+  , candlesGranularity     :: Maybe Granularity+  , candlesDailyAlignment  :: Maybe Int+  , candlesAlignmentTZ     :: Maybe Text+  , candlesWeeklyAlignment :: Maybe DayOfWeek   } deriving (Show)  candlesArgs :: CandlesArgs-candlesArgs = CandlesArgs (Count 500) S5 17 "America/New_York" Friday+candlesArgs = CandlesArgs Nothing Nothing Nothing Nothing Nothing  data CandlesCount = Count Int-                  | StartEnd { start :: ZonedTime-                             , end   :: ZonedTime-                             , includeFirst :: Bool-                             }+                  | StartEnd+                    { start :: ZonedTime+                    , end :: ZonedTime+                    , includeFirst :: Bool+                    }+                  | StartCount+                    { start :: ZonedTime+                    , count :: Int+                    , includeFirst :: Bool+                    }+                  | EndCount+                    { end :: ZonedTime+                    , count :: Int+                    }                   deriving (Show)  data DayOfWeek = Monday@@ -190,10 +204,38 @@                  | M                  deriving (Show) +-- | Utility function to convert Granularity to NominalDiffTime. __NOTE__: The+-- conversion from month to NominalDiffTime is not correct in general; we just+-- assume 31 days in a month, which is obviously false for 5 months of the+-- year.+granularityToDiffTime :: Granularity -> NominalDiffTime+granularityToDiffTime S5 = fromSeconds' 5+granularityToDiffTime S10 = fromSeconds' 10+granularityToDiffTime S15 = fromSeconds' 15+granularityToDiffTime S30 = fromSeconds' 30+granularityToDiffTime M1 = fromSeconds' $ 1 * 60+granularityToDiffTime M2 = fromSeconds' $ 2 * 60+granularityToDiffTime M3 = fromSeconds' $ 3 * 60+granularityToDiffTime M4 = fromSeconds' $ 4 * 60+granularityToDiffTime M5 = fromSeconds' $ 5 * 60+granularityToDiffTime M10 = fromSeconds' $ 10 * 60+granularityToDiffTime M15 = fromSeconds' $ 15 * 60+granularityToDiffTime M30 = fromSeconds' $ 30 * 60+granularityToDiffTime H1 = fromSeconds' $ 1 * 60 * 60+granularityToDiffTime H2 = fromSeconds' $ 2 * 60 * 60+granularityToDiffTime H3 = fromSeconds' $ 3 * 60 * 60+granularityToDiffTime H4 = fromSeconds' $ 4 * 60 * 60+granularityToDiffTime H6 = fromSeconds' $ 6 * 60 * 60+granularityToDiffTime H8 = fromSeconds' $ 8 * 60 * 60+granularityToDiffTime H12 = fromSeconds' $ 12 * 60 * 60+granularityToDiffTime D = fromSeconds' $ 1 * 60 * 60 * 24+granularityToDiffTime W = fromSeconds' $ 7 * 60 * 60 * 24+granularityToDiffTime M = fromSeconds' $ 31 * 60 * 60 * 24+ -- | Utility type for `midpointCandles` function response. Not exported. data MidpointCandlesResponse = MidpointCandlesResponse   { _midcandlesResponseInstrument  :: InstrumentText-  , _midcandlesResponseGranularity :: String+  , _midcandlesResponseGranularity :: Text   , _midcandlesResponseCandles     :: V.Vector MidpointCandlestick   } deriving (Show, Generic) @@ -205,7 +247,7 @@ -- | Utility type for `bidaskCandles` function response. Not exported. data BidAskCandlesResponse = BidAskCandlesResponse   { _bidaskResponseInstrument  :: InstrumentText-  , _bidaskResponseGranularity :: String+  , _bidaskResponseGranularity :: Text   , _bidaskResponseCandles     :: V.Vector BidAskCandlestick   } deriving (Show, Generic) 
src/OANDA/Trades.hs view
@@ -5,25 +5,20 @@ -- API.  module OANDA.Trades-       ( Trade (..)-       , openTrades-       , tradeInfo-       ) where+  ( Trade (..)+  , openTrades+  , tradeInfo+  ) where -import           Data.Aeson-import           Data.Decimal-import           Data.Time (ZonedTime) import qualified Data.Vector as V-import           GHC.Generics (Generic) -import           OANDA.Types-import           OANDA.Util+import OANDA.Internal  data Trade = Trade   { tradeId             :: !Int   , tradeUnits          :: !Int   , tradeSide           :: !Side-  , tradeInstrument     :: !String+  , tradeInstrument     :: !Text   , tradeTime           :: !ZonedTime   , tradePrice          :: !Decimal   , tradeTakeProfit     :: !Decimal@@ -37,17 +32,16 @@  -- | Gets a list of all open trades in an account. openTrades :: OandaEnv -> AccountID -> IO (V.Vector Trade)-openTrades od (AccountID aid) =-  do let url = baseURL od ++ "/v1/accounts/" ++ show aid ++ "/trades"-         opts = constructOpts od []-     jsonResponseArray url opts "trades"-+openTrades od (AccountID aid) = do+  let url = "GET " ++ baseURL od ++ "/v1/accounts/" ++ show aid ++ "/trades"+  request <- constructRequest od url []+  jsonResponseArray request "trades"  type TradeID = Int  -- | Get info for a specific trade. tradeInfo :: OandaEnv -> AccountID -> TradeID -> IO Trade-tradeInfo od (AccountID aid) tid =-  do let url = baseURL od ++ "/v1/accounts/" ++ show aid ++ "/trades/" ++ show tid-         opts = constructOpts od []-     jsonResponse url opts+tradeInfo od (AccountID aid) tid = do+  let url = "GET " ++ baseURL od ++ "/v1/accounts/" ++ show aid ++ "/trades/" ++ show tid+  request <- constructRequest od url []+  jsonResponse request
src/OANDA/Transactions.hs view
@@ -6,18 +6,13 @@ -- History> section of the API.  module OANDA.Transactions-       ( Transaction (..)-       , transactionHistory-       ) where+  ( Transaction (..)+  , transactionHistory+  ) where -import           Data.Aeson-import           Data.Decimal-import           Data.Time (ZonedTime) import qualified Data.Vector as V-import           GHC.Generics (Generic) -import           OANDA.Types-import           OANDA.Util+import OANDA.Internal  -- data TransactionArgs = TransactionArgs --   { transactionsMaxID      :: Maybe Integer@@ -30,9 +25,9 @@ -- | Get a list of transactions for the account. transactionHistory :: OandaEnv -> AccountID -> IO (V.Vector Transaction) transactionHistory od (AccountID aid) =-  do let url = baseURL od ++ "/v1/accounts/" ++ show aid ++ "/transactions"-         opts = constructOpts od []-     jsonResponseArray url opts "transactions"+  do let url = "GET " ++ baseURL od ++ "/v1/accounts/" ++ show aid ++ "/transactions"+     request <- constructRequest od url []+     jsonResponseArray request "transactions"   data Transaction = Transaction
− src/OANDA/Types.hs
@@ -1,92 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}---- | Defines types used in the REST API--module OANDA.Types-       ( OandaEnv-       , apiType-       , accessToken-       , sandboxAuth-       , practiceAuth-       , liveAuth-       , APIType (..)-       , apiEndpoint-       , AccessToken (..)-       , AccountID (..)-       , Side (..)-       , InstrumentText-       ) where--#if !MIN_VERSION_base(4,8,0)-import           Control.Applicative (pure)-#endif--import           Control.Monad (mzero)-import           Data.Aeson-import           Data.ByteString (ByteString)-import           Data.Text (Text, unpack)-import           GHC.Generics (Generic)----- | Wraps an `APIType` and an `AccessToken`. Mainly just a convenience wrapper--- to make functions have fewer arguments. To instantiate this type, use the--- `sandboxAuth`, `practiceAuth`, or `liveAuth` functions.-data OandaEnv = OandaEnv-  { apiType     :: APIType-  , accessToken :: Maybe AccessToken-  } deriving (Show)---- | Use the sandbox API.-sandboxAuth :: OandaEnv-sandboxAuth = OandaEnv Sandbox Nothing---- | Use the practice API.-practiceAuth :: AccessToken -> OandaEnv-practiceAuth = OandaEnv Practice . Just---- | Use the live API.-liveAuth :: AccessToken -> OandaEnv-liveAuth = OandaEnv Live . Just----- | The three endpoint types used in the REST API. See the following link for--- details: <http://developer.oanda.com/rest-live/development-guide/>-data APIType = Sandbox-             | Practice-             | Live-             deriving (Show)---- | Specifies the endpoints for each `APIType`. These are the base URLs for--- each API call.-apiEndpoint :: APIType -> String-apiEndpoint Sandbox  = "http://api-sandbox.oanda.com"-apiEndpoint Practice = "https://api-fxpractice.oanda.com"-apiEndpoint Live     = "https://api-fxtrade.oanda.com"---- | The token given by OANDA used to access the API-newtype AccessToken = AccessToken { unAccessToken :: ByteString }-                      deriving (Show)----- | Integer representing the Account ID of an account-newtype AccountID = AccountID { unAccountID :: Int}-                    deriving (Show)---- | Used when reporting a position in the API-data Side = Buy-          | Sell-          deriving (Show, Generic)--instance FromJSON Side where-  parseJSON (String s) = fmap readSide (pure $ unpack s)-  parseJSON _          = mzero---readSide :: String -> Side-readSide "buy"  = Buy-readSide "sell" = Sell-readSide _      = error "No parse Side"--type InstrumentText = Text
− src/OANDA/Util.hs
@@ -1,111 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}---- | Utility functions.--module OANDA.Util-       ( constructOpts-       , baseURL-       , makeParams-       , commaList-       , jsonOpts-       , jsonResponse-       , jsonResponseArray-       , jsonDelete-       , formatTimeRFC3339-       ) where---#if !MIN_VERSION_base(4,8,0)-import           Control.Applicative (Applicative, pure)-import           System.Locale (defaultTimeLocale)-#endif--import           Control.Lens-import           Data.Aeson-import qualified Data.Aeson.TH as TH-import           Data.ByteString (append)-import           Data.Char (toLower)-import           Data.Decimal-import qualified Data.Map as Map-import           Data.Monoid (-#if !MIN_VERSION_base(4,8,0)-  mempty,-#endif-  (<>))-import           Data.Scientific-import           Data.Text (Text, intercalate, unpack)-import           Data.Time-import           Network.Wreq--import           OANDA.Types---- | Convenience wrapper around `apiEndpoint`.-baseURL :: OandaEnv -> String-baseURL env = apiEndpoint (apiType env)---- | Create options for Wreq `getWith` using access token and params.-constructOpts :: OandaEnv -> [(Text, [Text])] -> Options-constructOpts env = constructOpts' (accessToken env)--constructOpts' :: Maybe AccessToken -> [(Text, [Text])] -> Options-constructOpts' maybeTok ps = defaults & params' & header'-  where params' = makeParams ps-        header' = maybe id makeHeader maybeTok-        makeHeader (AccessToken t) = header "Authorization" .~ ["Bearer " `append` t]----- | Create a valid list of params for wreq.-makeParams :: [(Text, [Text])] -> Options -> Options-makeParams xs = params .~ params'-  where params' = [(name, commaList p) | (name, p) <- xs]----- | Convert a Maybe [Text] item into empty text or comma-separated text.-commaList :: [Text] -> Text-commaList = intercalate ","----- | Used to derive FromJSON instances.-jsonOpts :: String -> TH.Options-jsonOpts s = TH.defaultOptions { TH.fieldLabelModifier = firstLower . drop (length s) }-  where firstLower [] = []-        firstLower (x:xs) = toLower x : xs---- | Boilerplate function to perform a request and extract the response body.-jsonResponse :: (FromJSON a) => String -> Options -> IO a-jsonResponse url opts =-  do r <- asJSON =<< getWith opts url-     return $ r ^. responseBody---- | Boilerplate function to perform a request and extract the response body.-jsonResponseArray :: (FromJSON a) => String -> Options -> String -> IO a-jsonResponseArray url opts name =-  do body <- jsonResponse url opts-     return $ body Map.! (name :: String)--jsonDelete :: (FromJSON a) => String -> Options -> IO a-jsonDelete url opts =-  do r <- asJSON =<< deleteWith opts url-     return $ r ^. responseBody---- | Formats time according to RFC3339 (which is the time format used by--- OANDA). Taken from the <https://github.com/HugoDaniel/timerep timerep> library.-formatTimeRFC3339 :: ZonedTime -> String-formatTimeRFC3339 zt@(ZonedTime _ z) = formatTime defaultTimeLocale "%FT%T" zt <> printZone-  where timeZoneStr = timeZoneOffsetString z-        printZone = if timeZoneStr == timeZoneOffsetString utc-                    then "Z"-                    else take 3 timeZoneStr <> ":" <> drop 3 timeZoneStr---instance (Integral a) => FromJSON (DecimalRaw a) where-  parseJSON (Number n) = readDecimalJSON n-  parseJSON (String s) = readDecimalJSON (read (unpack s))-  parseJSON _          = mempty---readDecimalJSON :: (Num i, Applicative f) => Scientific -> f (DecimalRaw i)-readDecimalJSON n = pure $ Decimal ((*) (-1) $ fromIntegral $ base10Exponent n)-                                    (fromIntegral $ coefficient n)
+ stack.yaml view
@@ -0,0 +1,3 @@+resolver: lts-7.9+packages:+  - '.'