-- | Defines the endpoints listed in the -- section of the API. module OANDA.Transactions where import qualified Data.ByteString.Char8 as BS8 import OANDA.Internal newtype OrderID = OrderID { unOrderID :: Int } deriving (Show, Eq) instance ToJSON OrderID where toJSON = toJSON . show . unOrderID instance FromJSON OrderID where parseJSON = fmap OrderID . parseJSONFromString newtype TransactionID = TransactionID { unTransactionID :: Int } deriving (Show, Eq) instance ToJSON TransactionID where toJSON = toJSON . show . unTransactionID instance FromJSON TransactionID where parseJSON = fmap TransactionID . parseJSONFromString newtype TradeID = TradeID { unTradeID :: Int } deriving (Show, Eq) instance ToJSON TradeID where toJSON = toJSON . show . unTradeID instance FromJSON TradeID where parseJSON = fmap TradeID . parseJSONFromString data OrderType = MARKET | LIMIT | STOP | MARKET_IF_TOUCHED | TAKE_PROFIT | STOP_LOSS | TRAILING_STOP_LOSS deriving (Show, Eq) deriveJSON defaultOptions ''OrderType data OrderState = PENDING | FILLED | TRIGGERED | CANCELLED deriving (Show, Eq) deriveJSON defaultOptions ''OrderState data ClientExtensions = ClientExtensions { clientExtensionsID :: Text , clientExtensionsTag :: Text , clientExtensionsComment :: Text } deriving (Show) deriveJSON (unPrefix "clientExtensions") ''ClientExtensions data TimeInForce = GTC | GTD | GFD | FOK | IOC deriving (Show, Eq) deriveJSON defaultOptions ''TimeInForce data OrderPositionFill = OPEN_ONLY | REDUCE_FIRST | REDUCE_ONLY | POSITION_DEFAULT deriving (Show, Eq) deriveJSON defaultOptions ''OrderPositionFill data MarketOrderPositionCloseout = MarketOrderPositionCloseout { marketOrderPositionCloseoutInstrument :: InstrumentName , marketOrderPositionCloseoutUnits :: Text } deriving (Show) deriveJSON (unPrefix "marketOrderPositionCloseout") ''MarketOrderPositionCloseout data MarketOrderTradeClose = MarketOrderTradeClose { marketOrderTradeCloseTradeID :: TradeID , marketOrderTradeCloseClientTradeID :: Text , marketOrderTradeCloseUnits :: Text } deriving (Show) deriveJSON (unPrefix "marketOrderTradeClose") ''MarketOrderTradeClose data MarketOrderMarginCloseout = MarketOrderMarginCloseout { marketOrderMarginCloseoutReason :: Text } deriving (Show) deriveJSON (unPrefix "marketOrderMarginCloseout") ''MarketOrderMarginCloseout data MarketOrderDelayedTradeClose = MarketOrderDelayedTradeClose { marketOrderDelayedTradeCloseTradeID :: TradeID , marketOrderDelayedTradeCloseClientTradeID :: Text , marketOrderDelayedTradeCloseSourceTransactionID :: TransactionID } deriving (Show) deriveJSON (unPrefix "marketOrderDelayedTradeClose") ''MarketOrderDelayedTradeClose data TakeProfitDetails = TakeProfitDetails { takeProfitDetailsPrice :: Text , takeProfitDetailsTimeInForce :: TimeInForce , takeProfitDetailsGtdTime :: OandaZonedTime , takeProfitDetailsClientExtensions :: Maybe ClientExtensions } deriving (Show) deriveJSON (unPrefix "takeProfitDetails") ''TakeProfitDetails data StopLossDetails = StopLossDetails { stopLossDetailsPrice :: Text , stopLossDetailsTimeInForce :: TimeInForce , stopLossDetailsGtdTime :: OandaZonedTime , stopLossDetailsClientExtensions :: Maybe ClientExtensions } deriving (Show) deriveJSON (unPrefix "stopLossDetails") ''StopLossDetails data TrailingStopLossDetails = TrailingStopLossDetails { trailingStopLossDetailsDistance :: Text , trailingStopLossDetailsTimeInForce :: TimeInForce , trailingStopLossDetailsGtdTime :: OandaZonedTime , trailingStopLossDetailsClientExtensions :: Maybe ClientExtensions } deriving (Show) deriveJSON (unPrefix "trailingStopLossDetails") ''TrailingStopLossDetails data TransactionType = CREATE | CLOSE | REOPEN | CLIENT_CONFIGURE | CLIENT_CONFIGURE_REJECT | TRANSFER_FUNDS | TRANSFER_FUNDS_REJECT | MARKET_ORDER | MARKET_ORDER_REJECT | LIMIT_ORDER | LIMIT_ORDER_REJECT | STOP_ORDER | STOP_ORDER_REJECT | MARKET_IF_TOUCHED_ORDER | MARKET_IF_TOUCHED_ORDER_REJECT | TAKE_PROFIT_ORDER | TAKE_PROFIT_ORDER_REJECT | STOP_LOSS_ORDER | STOP_LOSS_ORDER_REJECT | TRAILING_STOP_LOSS_ORDER | TRAILING_STOP_LOSS_ORDER_REJECT | ORDER_FILL | ORDER_CANCEL | ORDER_CANCEL_REJECT | ORDER_CLIENT_EXTENSIONS_MODIFY | ORDER_CLIENT_EXTENSIONS_MODIFY_REJECT | TRADE_CLIENT_EXTENSIONS_MODIFY | TRADE_CLIENT_EXTENSIONS_MODIFY_REJECT | MARGIN_CALL_ENTER | MARGIN_CALL_EXTEND | MARGIN_CALL_EXIT | DELAYED_TRADE_CLOSURE | DAILY_FINANCING | RESET_RESETTABLE_PL deriving (Show, Eq) deriveJSON defaultOptions ''TransactionType data TradeOpen = TradeOpen { tradeOpenTradeID :: TradeID , tradeOpenUnits :: Decimal , tradeOpenClientExtensions :: Maybe ClientExtensions } deriving (Show) deriveJSON (unPrefix "tradeOpen") ''TradeOpen data TradeReduce = TradeReduce { tradeReduceTradeID :: TradeID , tradeReduceUnits :: Decimal , tradeReduceRealizedPL :: AccountUnits , tradeReduceFinancing :: AccountUnits } deriving (Show) deriveJSON (unPrefix "tradeReduce") ''TradeReduce data OpenTradeFinancing = OpenTradeFinancing { openTradeFinancingTradeID :: TradeID , openTradeFinancingFinancing :: AccountUnits } deriving (Show) deriveJSON (unPrefix "openTradeFinancing") ''OpenTradeFinancing data PositionFinancing = PositionFinancing { -- BUG: Their docs say instrumentID but the field is actually called instrument positionFinancingInstrument :: InstrumentName , positionFinancingFinancing :: AccountUnits , positionFinancingOpenTradeFinancings :: [OpenTradeFinancing] } deriving (Show) deriveJSON (unPrefix "positionFinancing") ''PositionFinancing data Transaction = Transaction { -- Common to all transactions transactionId :: TransactionID , transactionTime :: OandaZonedTime , transactionAccountID :: AccountID , transactionUserID :: Integer , transactionBatchID :: TransactionID , transactionType :: TransactionType -- Specific to individual transactions , transactionDivisionID :: Maybe Integer , transactionSiteID :: Maybe Integer , transactionAccountUserID :: Maybe Integer , transactionAccountNumber :: Maybe Integer , transactionHomeCurrency :: Maybe Currency , transactionAlias :: Maybe Text , transactionMarginRate :: Maybe Decimal , transactionRejectReason :: Maybe Text , transactionAmount :: Maybe AccountUnits , transactionFundingReason :: Maybe Text , transactionAccountBalance :: Maybe AccountUnits , transactionInstrument :: Maybe InstrumentText , transactionUnits :: Maybe Decimal , transactionPrice :: Maybe Decimal , transactionTimeInForce :: Maybe TimeInForce , transactionPriceBound :: Maybe Text , transactionPositionFill :: Maybe Text , transactionMarketOrderTradeClose :: Maybe MarketOrderTradeClose , transactionLongPositionCloseout :: Maybe MarketOrderPositionCloseout , transactionShortPositionCloseout :: Maybe MarketOrderPositionCloseout , transactionMarginCloseout :: Maybe MarketOrderMarginCloseout , transactionDelayedTradeClose :: Maybe MarketOrderDelayedTradeClose , transactionReason :: Maybe Text , transactionClientExtensions :: Maybe ClientExtensions , transactionTakeProfitOnFill :: Maybe TakeProfitDetails , transactionStopLossOnFill :: Maybe StopLossDetails , transactionTrailingStopLossOnFill :: Maybe TrailingStopLossDetails , transactionTradeClientExtensions :: Maybe ClientExtensions , transactionGtdTime :: Maybe OandaZonedTime , transactionReplacesOrderID :: Maybe OrderID , transactionReplacedOrderCancelTransactionID :: Maybe TransactionID , transactionIntendedReplacesOrderID :: Maybe OrderID , transactionDistance :: Maybe Text , transactionOrderID :: Maybe OrderID , transactionClientOrderID :: Maybe Text , transactionPl :: Maybe AccountUnits , transactionFinancing :: Maybe AccountUnits , transactionTradeOpened :: Maybe TradeOpen , transactionTradesClosed :: Maybe [TradeReduce] , transactionTradeReduced :: Maybe TradeReduce , transactionTradeClientExtensionsModify :: Maybe ClientExtensions , transactionExtensionNumber :: Maybe Integer , transactionTradeIDs :: Maybe TradeID , transactionAccountFinancingMode :: Maybe Text , transactionPositionFinancings :: Maybe [PositionFinancing] } deriving (Show) deriveJSON (unPrefix "transaction") ''Transaction oandaTransaction :: OandaEnv -> AccountID -> TransactionID -> OANDARequest Transaction oandaTransaction env (AccountID accountId) (TransactionID transId) = OANDARequest $ baseApiRequest env "GET" ("/v3/accounts/" ++ accountId ++ "/transactions/" ++ show transId) data TransactionsSinceIDResponse = TransactionsSinceIDResponse { transactionsSinceIDResponseTransactions :: [Transaction] , transactionsSinceIDResponseLastTransactionID :: TransactionID } deriving (Show) deriveJSON (unPrefix "transactionsSinceIDResponse") ''TransactionsSinceIDResponse oandaTransactionsSinceID :: OandaEnv -> AccountID -> TransactionID -> OANDARequest TransactionsSinceIDResponse oandaTransactionsSinceID env (AccountID accountId) (TransactionID transId) = OANDARequest request where request = baseApiRequest env "GET" ("/v3/accounts/" ++ accountId ++ "/transactions/sinceid") & setRequestQueryString [("id", Just $ BS8.pack $ show transId)] data TransactionHeartbeat = TransactionHeartbeat { transactionHeartbeatLastTransactionID :: TransactionID , transactionHeartbeatTime :: OandaZonedTime } deriving (Show) deriveJSON (unPrefix "transactionHeartbeat") ''TransactionHeartbeat data TransactionsStreamResponse = StreamTransactionHeartbeat TransactionHeartbeat | StreamTransaction Transaction deriving (Show) -- The ToJSON instance is just for debugging, it's not actually correct deriveToJSON defaultOptions ''TransactionsStreamResponse instance FromJSON TransactionsStreamResponse where parseJSON (Object o) = do type' <- o .: "type" :: Parser String case type' of "HEARTBEAT" -> StreamTransactionHeartbeat <$> parseJSON (Object o) _ -> StreamTransaction <$> parseJSON (Object o) parseJSON _ = mempty oandaTransactionStream :: OandaEnv -> AccountID -> OANDAStreamingRequest TransactionsStreamResponse oandaTransactionStream env (AccountID accountId) = OANDAStreamingRequest $ baseStreamingRequest env "GET" ("/v3/accounts/" ++ accountId ++ "/transactions/stream")