From d1d902f91a80bdde589f2329a375b8906513c76f Mon Sep 17 00:00:00 2001 From: artin Date: Tue, 11 Jul 2023 11:45:37 +0000 Subject: [PATCH] initial load --- index.js | 354 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 17 +++ 2 files changed, 371 insertions(+) create mode 100644 index.js create mode 100644 package.json diff --git a/index.js b/index.js new file mode 100644 index 0000000..db427a9 --- /dev/null +++ b/index.js @@ -0,0 +1,354 @@ +const express = require('express') +var cors = require('cors') +var fs = require('fs') +var morgan = require('morgan') +var path = require('path') +const app = express() +const port = 3000 +const host = "0.0.0.0" + + +var accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' }) + +app.use(morgan('combined', { stream: accessLogStream })) + +const axios = require('axios').default; + + +const APIURL = 'https://api-fxpractice.oanda.com/v3' +const accounts = { + '1': { + 'ACCT': '101-001-8005237-001', + 'APIKEY': '85397a3ddc7b96ef57b90c9feda6d410-18f0f2b634f9f05533d2c20046bb79df' + }, + '2': { + 'APIKEY': 'b954456a3f4ac735de2555e1af50abf7-ed83ace2f9fb86412b76608daefc73a5', + 'ACCT': '101-001-23367262-002' + }, + '3': { + 'APIKEY': 'd4ea6095fe8017841279416437520aee-fa23a0556fb501520ceedbff5f405267', + 'ACCT': '101-002-26241098-001' + } +} + +async function getTrades(acct_id, api_key) { + try { + const response = await axios.request({ + url: `${APIURL}/accounts/${acct_id}/openTrades`, + method: 'get', + headers: { + 'Authorization': `Bearer ${api_key}` + } + }); + return (response) + } catch (error) { + console.error(error); + } +} +async function getTradesByInstrument(acct_id, api_key, instrument) { + try { + const response = await axios.request({ + url: `${APIURL}/accounts/${acct_id}/trades?instrument=${instrument}`, + method: 'get', + headers: { + 'Authorization': `Bearer ${api_key}` + } + }); + console.log(response) + if (response.data.trades.length == 0) { + return + } else { + + return (response.data.trades[0]) + } + } catch (error) { + console.error(error); + } +} + +async function getPositions(acct_id, api_key) { + try { + const response = await axios.request({ + url: `${APIURL}/accounts/${acct_id}/openPositions`, + method: 'get', + headers: { + 'Authorization': `Bearer ${api_key}` + } + }); + return (response); + } catch (error) { + console.error(error); + } +} + +async function order(acct_id, api_key, instrument, quantity) { + try { + + + let dist = "0.005" + let pdist = "0.005" + + if (instrument.includes("JPY")) { + dist = "0.5" + pdist = "0.5" + } + + data = { + "order": { + "trailingStopLossOnFill": { + "timeInForce": "GTC", + "distance": dist + }, + /*"takeProfitOnFill": { + "distance": pdist + },*/ + "timeInForce": "FOK", + "instrument": instrument, + "units": quantity, + "type": "MARKET", + "positionFill": "DEFAULT" + } + }; + const response = await axios.request({ + url: `${APIURL}/accounts/${acct_id}/orders`, + method: 'post', + headers: { + 'Authorization': `Bearer ${api_key}` + }, + data: data + }); + console.log(data) + return (response) + /*try { + delta = response.data.orderFillTransaction.price * .00164; + price = response.data.orderFillTransaction.price; + units = response.data.orderFillTransaction.units; + pres = price.toString().split('.')[1].length + if (units > 0) { + price = price - delta; + } else { + price = price + delta + } + return await stopLoss(acct_id, api_key, response.data.orderFillTransaction.id, Number(price).toFixed(pres).toString()) + } catch (error) { + console.log(error) + return (error) + }*/ + + } catch (error) { + console.error(error); + } +} + + + +async function closeOrder(acct_id, api_key, tradeID) { + try { + const response = await axios.request({ + url: `${APIURL}/accounts/${acct_id}/trades/${tradeID}/close`, + method: 'put', + headers: { + 'Authorization': `Bearer ${api_key}` + }, + }); + console.log(response.data); + return (response); + } catch (error) { + console.error(error); + } + +} + +async function trailingStopLoss(acct_id, api_key, instrument) { + const trade = await getTradesByInstrument(acct_id, api_key, instrument) + const tradeID = trade.id; + console.log(tradeID); + if (!tradeID) { + return + } + dist = "0.00164" + if (instrument.includes("JPY")) { + dist = "0.16" + } + try { + data = { + "trailingStopLoss": { + "timeInForce": "GTC", + "distance": dist + } + }; + const response = await axios.request({ + url: `${APIURL}/accounts/${acct_id}/trades/${tradeID}/orders`, + method: 'put', + headers: { + 'Authorization': `Bearer ${api_key}` + }, + data: data + }); + console.log(data) + console.log(response.data); + return (response); + } catch (error) { + console.error(error); + } + +} + +async function stopLoss(acct_id, api_key, tradeID, price) { + try { + data = { + "stopLoss": { + "timeInForce": "GTC", + "distance": "0.30" + //"price": price + } + }; + const response = await axios.request({ + url: `${APIURL}/accounts/${acct_id}/trades/${tradeID}/orders`, + method: 'put', + headers: { + 'Authorization': `Bearer ${api_key}` + }, + data: data + }); + console.log(data) + console.log(response.data); + return (response); + } catch (error) { + console.error(error); + } + +} + +async function takeProfit(acct_id, api_key, tradeID, dist) { + try { + data = { + "takeProfit": { + "timeInForce": "GTC", + "distance": dist + } + }; + const response = await axios.request({ + url: `${APIURL}/accounts/${acct_id}/trades/${tradeID}/orders`, + method: 'put', + headers: { + 'Authorization': `Bearer ${api_key}` + }, + data: data + }); + return (response); + } catch (error) { + console.error(error); + } + +} + +app.get('/closeAll', async (req, res) => { + + for (account of Object.keys(accounts)) { + let trades = await getTrades(accounts[account]['ACCT'], accounts[account]['APIKEY']) + for (const t of trades.data['trades']) { + await closeOrder(accounts[account]['ACCT'], accounts[account]['APIKEY'], t['id']) + } + } + + res.json('done') +}) + + +app.get('/tradesData', async (req, res) => { + + let r = []; + for (account of Object.keys(accounts)) { + let response = await getTrades(accounts[account]['ACCT'], accounts[account]['APIKEY']) + try { + //Object.(response.data['trades']).forEach(([a, t]) =>{ + response.data['trades'].forEach(( t) =>{ + + t["Account"] = account; + delete t['lastTransactionID']; + delete t['trailingStopLossOrder']; +console.log(t); + r.push(t) +}); + +} catch(error) { +console.log(error) +} + } + res.header('Access-Control-Allow-Origin', '*') + res.json(r) +}) + + +app.get('/trades', async (req, res) => { + + let r = {} + for (account of Object.keys(accounts)) { + let response = await getTrades(accounts[account]['ACCT'], accounts[account]['APIKEY']) + try { + r[account] = response.data; +} catch(error) { +console.log(error) +} + } + res.header('Access-Control-Allow-Origin', '*') + res.json(r) +}) +app.get('/trailingStop/:instrument', async (req, res) => { + const response = await trailingStopLoss(ACCT, APIKEY, req.params.instrument); + console.log(response); + res.header('Access-Control-Allow-Origin', '*') + res.json(response.data) +}) +app.get('/tradesByInstrument/:instrument', async (req, res) => { + res.header('Access-Control-Allow-Origin', '*') + res.json(await getTradesByInstrument(ACCT, APIKEY, req.params.instrument)) +}) +app.get('/order/:instrument/:quantity', async (req, res) => { + + + r = {}; + /*td = await getTradesByInstrument(accounts['1']['ACCT'], accounts['1']['APIKEY'], req.params.instrument); + if (td) { + + if (td["initialUnits"] != req.params.quantity) { + let dist = "0.005" + if (req.params.instrument.includes("JPY")) { + let dist = "0.5" + } + await takeProfit(accounts['1']['ACCT'], accounts['1']['APIKEY'], td["id"], dist) + } + } else { + response = await order(accounts['1']['ACCT'], accounts['1']['APIKEY'], req.params.instrument, req.params.quantity); + }*/ + /*td = await getTradesByInstrument(accounts['2']['ACCT'], accounts['2']['APIKEY'], req.params.instrument); + if (td) { + + if (td["initialUnits"] != req.params.quantity) { + let dist = "0.005" + if (req.params.instrument.includes("JPY")) { + + let dist = "0.5" + } + await takeProfit(accounts['2']['ACCT'], accounts['2']['APIKEY'], td["id"], dist) + } + } else { + + response = await order(accounts['2']['ACCT'], accounts['2']['APIKEY'], req.params.instrument, req.params.quantity); + } */ + for (account of Object.keys(accounts)) { + response = await order(accounts[account]['ACCT'], accounts[account]['APIKEY'], req.params.instrument, req.params.quantity); + } + + + res.header('Access-Control-Allow-Origin', '*') + + + res.json("ok") + +}) + +app.listen(port, host, () => { + console.log(`osapi started`); +}) diff --git a/package.json b/package.json new file mode 100644 index 0000000..b4a10b7 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "osapi", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^1.4.0", + "cors": "^2.8.5", + "express": "^4.18.2", + "morgan": "^1.10.0" + } +}