factor App.java into domain classes
This commit is contained in:
@ -20,6 +20,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.espertech:esper:7.1.0'
|
implementation 'com.espertech:esper:7.1.0'
|
||||||
implementation 'com.oanda.v20:v20:3.0.21'
|
implementation 'com.oanda.v20:v20:3.0.21'
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.5'
|
||||||
|
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-csv:2.9.5'
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|||||||
18
src/main/java/AccountInfo.java
Normal file
18
src/main/java/AccountInfo.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
|
||||||
|
public class AccountInfo {
|
||||||
|
|
||||||
|
// TODO use properties file
|
||||||
|
|
||||||
|
public String accountID() {
|
||||||
|
return "101-001-7935538-001";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLiveAccount() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String accessToken() {
|
||||||
|
return "9a480f0b83e987f4015cf0846790c7d9-695ced635526744abd61bdf0e2ae8b71";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,75 +1,30 @@
|
|||||||
import java.io.BufferedReader;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.net.URLEncoder;
|
|
||||||
|
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
//import com.espertech.esper.client.*;
|
|
||||||
|
|
||||||
|
import com.espertech.esper.client.EPServiceProvider;
|
||||||
|
import com.espertech.esper.client.EPServiceProviderManager;
|
||||||
|
import com.fasterxml.jackson.core.JsonParseException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
|
import com.fasterxml.jackson.databind.MappingIterator;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectReader;
|
||||||
|
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
|
||||||
|
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
//EPServiceProvider esper = EPServiceProviderManager.getDefaultProvider();
|
||||||
|
|
||||||
//EPServiceProvider engine = EPServiceProviderManager.getDefaultProvider();
|
File f = new File("/home/alx/Nextcloud/projects/ATS_Esper/EURUSD-2017-01-small.csv");
|
||||||
|
// new CSVReader(f).run(new DebugProcessor());
|
||||||
|
|
||||||
try {
|
AccountInfo acctInfo = new AccountInfo();
|
||||||
String[] instruments = new String[] { "USD_CAD", "EUR_USD", "USD_JPY" };
|
String[] instruments = new String[] { "USD_CAD", "EUR_USD", "USD_JPY" };
|
||||||
|
new OANDAReader(acctInfo, instruments).run(new DebugProcessor());
|
||||||
String query = String.format("instruments=%s",
|
|
||||||
URLEncoder.encode(String.join(",", instruments), "UTF-8"));
|
|
||||||
|
|
||||||
openConnection("https://stream-fxpractice.oanda.com/v3/accounts/101-001-7935538-001/pricing/stream?" + query);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void openConnection(String urlStr)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
HttpsURLConnection httpConn = null;
|
|
||||||
String line = null;
|
|
||||||
try {
|
|
||||||
URL url = new URL(urlStr);
|
|
||||||
URLConnection urlConn = url.openConnection();
|
|
||||||
if (!(urlConn instanceof HttpsURLConnection)) {
|
|
||||||
throw new IOException ("URL is not an Https URL");
|
|
||||||
}
|
|
||||||
httpConn = (HttpsURLConnection)urlConn;
|
|
||||||
httpConn.setAllowUserInteraction(false);
|
|
||||||
httpConn.setInstanceFollowRedirects(true);
|
|
||||||
httpConn.setRequestMethod("GET");
|
|
||||||
httpConn.setReadTimeout(50 * 1000);
|
|
||||||
httpConn.setRequestProperty("Authorization", "Bearer 9a480f0b83e987f4015cf0846790c7d9-695ced635526744abd61bdf0e2ae8b71");
|
|
||||||
BufferedReader is =
|
|
||||||
new BufferedReader(new InputStreamReader(httpConn.getInputStream()));
|
|
||||||
|
|
||||||
while ((line = is.readLine( )) != null) {
|
|
||||||
System.out.println(line);
|
|
||||||
// Message msg = Message.obtain();
|
|
||||||
// msg.what=1;
|
|
||||||
// msg.obj=line;
|
|
||||||
// mhandler.sendMessage(msg);
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch(SocketTimeoutException e){
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
//Message msg = Message.obtain();
|
|
||||||
//msg.what=2;
|
|
||||||
//BufferedInputStream in = new BufferedInputStream(httpConn.getErrorStream());
|
|
||||||
//line = new String(readStream(in));
|
|
||||||
//msg.obj = line;
|
|
||||||
//mhandler.sendMessage(msg);
|
|
||||||
} finally {
|
|
||||||
httpConn.disconnect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/main/java/CSVReader.java
Normal file
32
src/main/java/CSVReader.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.espertech.esper.client.EPServiceProvider;
|
||||||
|
import com.fasterxml.jackson.databind.MappingIterator;
|
||||||
|
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
|
||||||
|
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
|
||||||
|
|
||||||
|
public class CSVReader /*implements TickStreamReader*/ {
|
||||||
|
File file;
|
||||||
|
|
||||||
|
|
||||||
|
public CSVReader(File file) {
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(TickProcessor processor) {
|
||||||
|
CsvMapper mapper = new CsvMapper();
|
||||||
|
CsvSchema schema = mapper.schemaFor(TrueFXTick.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
MappingIterator<TrueFXTick> it = mapper.readerFor(TrueFXTick.class).with(schema).readValues(file);
|
||||||
|
while (it.hasNextValue()) {
|
||||||
|
TrueFXTick value = it.nextValue();
|
||||||
|
processor.process(value);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/main/java/DebugProcessor.java
Normal file
7
src/main/java/DebugProcessor.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
public class DebugProcessor implements TickProcessor {
|
||||||
|
public void process(Tick tick) {
|
||||||
|
System.out.println(tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
100
src/main/java/OANDAReader.java
Normal file
100
src/main/java/OANDAReader.java
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
import javax.crypto.spec.OAEPParameterSpec;
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParseException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
|
||||||
|
public class OANDAReader {
|
||||||
|
AccountInfo accountInfo;
|
||||||
|
String[] instruments;
|
||||||
|
|
||||||
|
|
||||||
|
public OANDAReader(AccountInfo accountInfo, String[] instruments) {
|
||||||
|
this.accountInfo = accountInfo;
|
||||||
|
this.instruments = instruments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(TickProcessor processor) {
|
||||||
|
readConnection(streamURL(), processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String streamURL() {
|
||||||
|
String type = accountInfo.isLiveAccount() ? "fxlive" : "fxpractice";
|
||||||
|
String instrList = String.join(",", instruments);
|
||||||
|
String query = "";
|
||||||
|
try {
|
||||||
|
query = String.format("instruments=%s", URLEncoder.encode(instrList, "UTF-8"));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format("https://stream-%s.oanda.com/v3/accounts/%s/pricing/stream?%s",
|
||||||
|
type, accountInfo.accountID(), query);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readConnection(String urlStr, TickProcessor processor) {
|
||||||
|
HttpsURLConnection httpConn = null;
|
||||||
|
String line = null;
|
||||||
|
try {
|
||||||
|
URL url = new URL(urlStr);
|
||||||
|
URLConnection urlConn = url.openConnection();
|
||||||
|
if (!(urlConn instanceof HttpsURLConnection)) {
|
||||||
|
throw new IOException ("URL is not an https URL");
|
||||||
|
}
|
||||||
|
httpConn = (HttpsURLConnection)urlConn;
|
||||||
|
httpConn.setAllowUserInteraction(false);
|
||||||
|
//httpConn.setInstanceFollowRedirects(true);
|
||||||
|
httpConn.setRequestMethod("GET");
|
||||||
|
//httpConn.setReadTimeout(50 * 1000);
|
||||||
|
httpConn.setRequestProperty("Authorization", "Bearer 9a480f0b83e987f4015cf0846790c7d9-695ced635526744abd61bdf0e2ae8b71");
|
||||||
|
BufferedReader is =
|
||||||
|
new BufferedReader(new InputStreamReader(httpConn.getInputStream()));
|
||||||
|
|
||||||
|
while ((line = is.readLine( )) != null) {
|
||||||
|
processLine(line, processor);
|
||||||
|
}
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch(SocketTimeoutException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
httpConn.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ObjectMapper mapper = new ObjectMapper(); // create once, reuse
|
||||||
|
|
||||||
|
private static void processLine(String line, TickProcessor processor) {
|
||||||
|
if (line.indexOf ("PRICE") > -1) {
|
||||||
|
OANDATick tick;
|
||||||
|
try {
|
||||||
|
tick = mapper.readValue(line, OANDATick.class);
|
||||||
|
processor.process(tick);
|
||||||
|
} catch (JsonParseException | JsonMappingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else if (line.indexOf ("HEARTBEAT") > -1) {
|
||||||
|
// ignore
|
||||||
|
} else {
|
||||||
|
System.out.println("Unknown type: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/main/java/OANDATick.java
Normal file
75
src/main/java/OANDATick.java
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The specification of an Account-specific Price.
|
||||||
|
*
|
||||||
|
* {"type":"PRICE","time":"2018-04-05T20:35:20.983907480Z","bids":[{"price":"1.22376","liquidity":10000000}],"asks":[{"price":"1.22386","liquidity":10000000}],"closeoutBid":"1.22361","closeoutAsk":"1.22401","status":"tradeable","tradeable":true,"instrument":"EUR_USD"}
|
||||||
|
*/
|
||||||
|
public class OANDATick implements Tick {
|
||||||
|
public String type;
|
||||||
|
@JsonFormat(pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||||
|
private Date time;
|
||||||
|
public PriceBucket[] bids;
|
||||||
|
public PriceBucket[] asks;
|
||||||
|
public BigDecimal closeoutBid;
|
||||||
|
public BigDecimal closeoutAsk;
|
||||||
|
public String status;
|
||||||
|
public boolean tradeable;
|
||||||
|
private String instrument;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the instrument
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getInstrument() {
|
||||||
|
return instrument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param instrument the instrument to set
|
||||||
|
*/
|
||||||
|
public void setInstrument(String instrument) {
|
||||||
|
this.instrument = instrument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the time
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Date getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param time the time to set
|
||||||
|
*/
|
||||||
|
public void setTime(Date time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the bid
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BigDecimal getBid() {
|
||||||
|
return bids[0].price;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the ask
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BigDecimal getAsk() {
|
||||||
|
return asks[0].price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return String.format("OANDATick[%s,%s]", getInstrument(), getBid());
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/main/java/PriceBucket.java
Normal file
16
src/main/java/PriceBucket.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PriceBucket represents a price available for an amount of liquidity.
|
||||||
|
*
|
||||||
|
* {"price":"1.22376","liquidity":10000000}
|
||||||
|
*/
|
||||||
|
public class PriceBucket {
|
||||||
|
public BigDecimal price;
|
||||||
|
public Integer liquidity;
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return String.format("PriceBucket[%f, %d]", price, liquidity);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/main/java/Tick.java
Normal file
26
src/main/java/Tick.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public interface Tick {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the instrument
|
||||||
|
*/
|
||||||
|
public String getInstrument();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the time
|
||||||
|
*/
|
||||||
|
public Date getTime();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the bid
|
||||||
|
*/
|
||||||
|
public BigDecimal getBid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the ask
|
||||||
|
*/
|
||||||
|
public BigDecimal getAsk();
|
||||||
|
}
|
||||||
3
src/main/java/TickProcessor.java
Normal file
3
src/main/java/TickProcessor.java
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
public interface TickProcessor {
|
||||||
|
public void process(Tick tick);
|
||||||
|
}
|
||||||
87
src/main/java/TrueFXTick.java
Normal file
87
src/main/java/TrueFXTick.java
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TrueFXTick holds a single tick read from TrueFX data archive csv
|
||||||
|
* file.
|
||||||
|
*
|
||||||
|
* A line looks like:
|
||||||
|
* EUR/USD,20170102 00:00:00.803,1.0523,1.05307
|
||||||
|
*/
|
||||||
|
@JsonPropertyOrder({ "instrument", "time", "bid", "ask" })
|
||||||
|
public class TrueFXTick implements Tick {
|
||||||
|
private String instrument;
|
||||||
|
@JsonFormat(pattern="yyyyMMdd HH:mm:ss.SSS")
|
||||||
|
private Date time;
|
||||||
|
private BigDecimal bid;
|
||||||
|
private BigDecimal ask;
|
||||||
|
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return String.format("TrueFXTick[%s,%s,%s,%s]", instrument, time, bid, ask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the instrument
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getInstrument() {
|
||||||
|
return instrument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param instrument the instrument to set
|
||||||
|
*/
|
||||||
|
public void setInstrument(String instrument) {
|
||||||
|
this.instrument = instrument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the time
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Date getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param time the time to set
|
||||||
|
*/
|
||||||
|
public void setTime(Date time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the bid
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BigDecimal getBid() {
|
||||||
|
return bid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bid the bid to set
|
||||||
|
*/
|
||||||
|
public void setBid(BigDecimal bid) {
|
||||||
|
this.bid = bid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the ask
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BigDecimal getAsk() {
|
||||||
|
return ask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ask the ask to set
|
||||||
|
*/
|
||||||
|
public void setAsk(BigDecimal ask) {
|
||||||
|
this.ask = ask;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user