From 2d7feed929c11fcedb88c26068fb3bd1c9d964e2 Mon Sep 17 00:00:00 2001 From: 7u83 <7u83@maiil.ru> Date: Sat, 5 Jan 2019 11:05:18 +0100 Subject: [PATCH] Work on correct calculation of available money with margin --- nbproject/project.properties | 2 +- src/opensesim/gui/account/StatusPanel.form | 28 ++++ src/opensesim/gui/account/StatusPanel.java | 78 ++++++++++ src/opensesim/world/Account.java | 157 ++++++++++++++------- src/opensesim/world/SimpleTrader.java | 38 ++--- src/opensesim/world/TradingEngine.java | 34 ++--- 6 files changed, 238 insertions(+), 99 deletions(-) create mode 100644 src/opensesim/gui/account/StatusPanel.form create mode 100644 src/opensesim/gui/account/StatusPanel.java diff --git a/nbproject/project.properties b/nbproject/project.properties index 6037054..aaab35f 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -1,4 +1,4 @@ -#Fri, 04 Jan 2019 17:38:20 +0100 +#Sat, 05 Jan 2019 10:43:36 +0100 annotation.processing.enabled=true annotation.processing.enabled.in.editor=false annotation.processing.processors.list= diff --git a/src/opensesim/gui/account/StatusPanel.form b/src/opensesim/gui/account/StatusPanel.form new file mode 100644 index 0000000..c638b68 --- /dev/null +++ b/src/opensesim/gui/account/StatusPanel.form @@ -0,0 +1,28 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/opensesim/gui/account/StatusPanel.java b/src/opensesim/gui/account/StatusPanel.java new file mode 100644 index 0000000..9a8f60f --- /dev/null +++ b/src/opensesim/gui/account/StatusPanel.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019, 7u83 <7u83@mail.ru> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package opensesim.gui.account; + +import opensesim.world.Account; + +/** + * + * @author 7u83 <7u83@mail.ru> + */ +public class StatusPanel extends javax.swing.JPanel { + + /** + * Creates new form StatusPanel + */ + public StatusPanel() { + initComponents(); + } + + public StatusPanel(Account account) { + this(); + setAccount(account); + } + + private Account account; + + public void setAccount(Account account) { + this.account = account; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 400, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 300, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables +} diff --git a/src/opensesim/world/Account.java b/src/opensesim/world/Account.java index a7dbca2..034f15a 100644 --- a/src/opensesim/world/Account.java +++ b/src/opensesim/world/Account.java @@ -41,19 +41,21 @@ import org.json.JSONObject; public class Account { HashMap assets = new HashMap<>(); - HashMap assets_avail = new HashMap<>(); + HashMap assets_bound = new HashMap<>(); HashMap stop_los = new HashMap<>(); - - public double margin_bound=0.0; + + public double margin_bound = 0.0; Trader owner; //public Exchange exchange = null; - - - private World world; - - - + + private RealWorld world; + private Exchange exchange; + + public Exchange getExchange() { + return exchange; + } + private boolean unlimited = false; public boolean isUnlimied() { @@ -63,7 +65,6 @@ public class Account { void setUnlimied(boolean unlimied) { this.unlimited = unlimied; } - private double leverage = 0.0; @@ -80,7 +81,7 @@ public class Account { } public Map getAssetsAavail() { - return Collections.unmodifiableMap(assets_avail); + return Collections.unmodifiableMap(assets_bound); } public Trader getOwner() { @@ -88,34 +89,40 @@ public class Account { } protected Account(World world) { - this.world = world; + this(world, null, null); } - protected Account(World world, JSONObject cfg) { - this.world = world; + protected Account(World world, Exchange exchange, JSONObject cfg) { + this.world = (RealWorld) world; + if (exchange == null) { + this.exchange = world.getDefaultExchange(); + } + if (cfg == null) { + return; + } } public Double getMargin(AbstractAsset currency) { - /* Double d = this.getAssetDebt(world.getDefaultExchange(), currency); + /* Double d = this.getAssetDebt(world.getDefaultExchange(), currency); Double f = this.getFinalBalance(currency) * getLeverage() ; System.out.printf("Debth %f - Final: %f Return margin %f\n", d,f, f-d); return f-d;*/ - - return this.getFinalBalance(currency) * getLeverage() + this.getFinalBalance(currency) + + return this.getFinalBalance(currency) * getLeverage() + this.getFinalBalance(currency) - this.getAssetDebt(world.getDefaultExchange(), currency); - } - + } + synchronized void add(AssetPack pack) { assets.put(pack.asset, get(pack.asset) + pack.volume); - assets_avail.put(pack.asset, getAvail(pack.asset) + pack.volume); + assets_bound.put(pack.asset, getAvail(pack.asset) + pack.volume); } synchronized void sub(AssetPack pack) { assets.put(pack.asset, get(pack.asset) - pack.volume); - // assets_avail.put(pack.asset, getAvail(pack.asset) - pack.volume); + // assets_bound.put(pack.asset, getAvail(pack.asset) - pack.volume); } public double get(AbstractAsset asset) { @@ -123,23 +130,22 @@ public class Account { } public double getAvail(AbstractAsset asset) { - if (this.getLeverage()>0){ + if (this.getLeverage() > 0) { Double margin = this.getMargin(world.getDefaultCurrency()); - + AssetPair ap = world.getAssetPair(asset, world.getDefaultCurrency()); - - + return margin / world.getDefaultExchange().getAPI(ap).getLastQuote().price; } - + return 0.0; - - //return assets_avail.getOrDefault(asset, 0.0); + + //return assets_bound.getOrDefault(asset, 0.0); } public void addAvail(AbstractAsset asset, double val) { double avail = getAvail(asset); - assets_avail.put(asset, (avail + val)); + assets_bound.put(asset, (avail + val)); } HashSet listeners = new HashSet<>(); @@ -163,7 +169,7 @@ public class Account { public Double getAssetDebt(Exchange ex, AbstractAsset currency) { Double result = 0.0; - System.out.printf("Enter depth rechner %f\n", result); + for (AbstractAsset a : assets.keySet()) { if (a.equals(currency)) { continue; @@ -177,17 +183,16 @@ public class Account { Double v = get(a) * api.last_quote.price; Double sl = this.calcStopLoss(a); Double n = get(a); - if (n==0.0) + if (n == 0.0) { continue; - - System.out.printf("Asset: %s - %f %f %f\n", a.getSymbol(),n, v, sl*n); - - - result = result + (v-sl*n); - System.out.printf("Result is now %f\n", result); + } + +// System.out.printf("Asset: %s - %f %f %f\n", a.getSymbol(), n, v, sl * n); + result = result + (v - sl * n); + // System.out.printf("Result is now %f\n", result); } - System.out.printf("Return Dresult %f\n", result); + // System.out.printf("Return Dresult %f\n", result); return result; } @@ -200,7 +205,8 @@ public class Account { * @return final balance * */ - public Double getFinalBalance(Exchange ex, AbstractAsset currency) { + public Double getFinalBalance(Exchange ex, AbstractAsset currency, + boolean bound) { Double result = get(currency); for (AbstractAsset a : assets.keySet()) { @@ -212,18 +218,25 @@ public class Account { if (pair == null) { continue; } - v = get(a); - if (v==0.0) + v = get(a) + (bound ? getBound(a) : 0.0); + + if (v == 0.0) { continue; - - - TradingEngine api = (TradingEngine) ex.getAPI(pair); - //v = get(a) * api.last_quote.price; + } - result = result + v*api.last_quote.price; + TradingEngine api = (TradingEngine) ex.getAPI(pair); + result = result + v * api.last_quote.price; } return result; } + + Double getBound(AbstractAsset asset){ + return assets_bound.getOrDefault(asset, 0.0); + } + + void addBound(AbstractAsset asset, Double vol){ + assets_bound.put(asset, getBound(asset)+vol); + } /** * Get the final balance as if all assets would be sold ob the default @@ -234,7 +247,7 @@ public class Account { * @return final balance */ public Double getFinalBalance(AbstractAsset currency) { - return getFinalBalance(world.getDefaultExchange(), currency); + return getFinalBalance(this.getExchange(), currency, false); } /** @@ -262,22 +275,58 @@ public class Account { TradingEngine api = (TradingEngine) ex.getAPI(pair); Double v = get(a) * api.last_quote.price; - e = e+v; - + e = e + v; + } - - return -(double)e / (double)get(asset); + + return -(double) e / (double) get(asset); } - public Double calcStopLoss(AbstractAsset asset){ - return calcStopLoss(world.getDefaultExchange(),asset,world.getDefaultAssetPair().getCurrency()); + public Double calcStopLoss(AbstractAsset asset) { + return calcStopLoss(world.getDefaultExchange(), asset, world.getDefaultAssetPair().getCurrency()); } /** * Return the world this account belongs to + * * @return world */ - public World getWorld(){ + public World getWorld() { return world; } + + private boolean isLeveraged() { + return getLeverage() > 0.0; + } + + boolean isCovered(AssetPair pair, double volume, double limit) { + if (this.isUnlimied()) { + return true; + } + + if (!this.isLeveraged()) { + if (limit == 0.0) { + // an unlimited order is always considered to be + // covereable. When the trade comes to execution, + // the limits will be checked. + return true; + } + if (volume < 0) { + // It's a limited sell order, we have just to check + // if a sufficient amount of assets is available + return getAvail(pair.getAsset()) + volume > 0; + } + // Check if enough money is available to cover the + // entiere volume to by + return getAvail(pair.getCurrency()) >= limit * volume; + } + + + Double margin = this.getMargin(pair.getCurrency()); + System.out.printf("Margin: %f > %f\n", margin, Math.abs(volume * limit)); + + return margin > 0; //Math.abs(volume * limit); + + } + } diff --git a/src/opensesim/world/SimpleTrader.java b/src/opensesim/world/SimpleTrader.java index e55c6ec..b496b39 100644 --- a/src/opensesim/world/SimpleTrader.java +++ b/src/opensesim/world/SimpleTrader.java @@ -105,17 +105,16 @@ public class SimpleTrader extends AbstractTrader implements EventListener { pack = new AssetPack(p.getCurrency(), 1000); account_b.add(pack); - account_b.setLeverage(10.0); + account_b.setLeverage(9.0); pack = new AssetPack(p.getCurrency(), 1234567890); account_1.add(pack); account_1.setLeverage(0.0); account_1.setUnlimied(true); - long delay = (long) (1000.0f * getWorld().randNextFloat(15.0f, 15.7f)); + long delay = (long) (1000.0f * getWorld().randNextFloat(5.0f, 5.7f)); setStatus(String.format("Initial delay: Sleeping for %d seconds.", delay)); - - + getWorld().schedule(this, delay); // long delay = (long) (getRandom(initial_delay[0], initial_delay[1]) * 1000); @@ -133,37 +132,30 @@ public class SimpleTrader extends AbstractTrader implements EventListener { long diff = getWorld().currentTimeMillis() - last_time; last_time = getWorld().currentTimeMillis(); - - AssetPair p = getWorld().getDefaultAssetPair(); - - - ex = getWorld().getDefaultExchange(); + + AssetPair p = getWorld().getDefaultAssetPair(); + + ex = getWorld().getDefaultExchange(); api = ex.getAPI(p); - AssetPair msftp = getWorld().getAssetPair(getWorld().getAssetBySymbol("MSFT"), - getWorld().getAssetBySymbol("EUR")); - TradingAPI mapi = ex.getAPI(msftp); - + AssetPair msftp = getWorld().getAssetPair(getWorld().getAssetBySymbol("MSFT"), + getWorld().getAssetBySymbol("EUR")); + TradingAPI mapi = ex.getAPI(msftp); + Order ob = api.createOrder(account_b, Order.Type.BUYLIMIT, 20, 100); //Order mob = mapi.createOrder(account_b, Order.Type.SELLLIMIT, 80, 100); - - Order oa = api.createOrder(account_1, Order.Type.SELLLIMIT, 100, 100); - //Order oaaa = mapi.createOrder(account_1, Order.Type.BUYLIMIT, 100, 100); - + Order oa = api.createOrder(account_1, Order.Type.SELLLIMIT, 100, 200); + //Order oaaa = mapi.createOrder(account_1, Order.Type.BUYLIMIT, 100, 100); //Order oax = mapi.createOrder(account_1, Order.Type.BUYLIMIT, 100, 100); - // Order oa = api.createOrder(account_1, Order.Type.BUYLIMIT, 100, 10.0); // Order ob = api.createOrder(account_1, Order.Type.BUYLIMIT, 100, 9.0); // Order oc = api.createOrder(account_1, Order.Type.BUYLIMIT, 100, 8.0); // Order o2 = api.createOrder(account_10, Order.Type.SELLLIMIT, 300, 1.0); //Order ou = api.createOrder(account_1, Order.Type.BUYLIMIT, 30, 10.0); // Order o1 = api.createOrder(account, Order.Type.SELLLIMIT, 250, 278); - - - - /* + /* System.out.printf("Here we are: %d - [%d]\n", Thread.currentThread().getId(), diff); getWorld().schedule(this, 1000); @@ -174,7 +166,7 @@ public class SimpleTrader extends AbstractTrader implements EventListener { api = ex.getAPI(p); Order o = api.createOrder(account, Order.Type.BUY, 112.987123, limit); limit += 12; -*/ + */ return -1; } diff --git a/src/opensesim/world/TradingEngine.java b/src/opensesim/world/TradingEngine.java index 4bdb44e..1d87b76 100644 --- a/src/opensesim/world/TradingEngine.java +++ b/src/opensesim/world/TradingEngine.java @@ -437,32 +437,24 @@ class TradingEngine implements TradingAPI { } else { switch (type) { case BUYLIMIT: { - Double avail; - - // verfify available currency for a buy limit order - AbstractAsset currency = this.assetpair.getCurrency(); - if (account.getLeverage() == 0.0) { - avail = account.getAvail(currency); - account.addAvail(currency, -(v * l)); - - } else { - - avail = account.getMargin(assetpair.getCurrency()); - + + if (!account.isCovered(assetpair, volume, limit)){ + System.out.printf("Not enough funds\n"); + return null; } - + // return if not enough funds are available - if (avail < v * l) { - o = new Order(this, account, type, v, l); - o.status = Order.Status.ERROR; +// if (avail < v * l) { +// o = new Order(this, account, type, v, l); +// o.status = Order.Status.ERROR; - System.out.printf("Error order no funds\n"); +// System.out.printf("Error order no funds\n"); // return o; - } + // } account.margin_bound += v * l; // reduce the available money -// account.assets_avail.put(currency, avail - v * l); +// account.assets_bound.put(currency, avail - v * l); //account.addMarginAvail(currency, -((v * l)/account.getLeverage())); order_limit = l; @@ -482,7 +474,7 @@ class TradingEngine implements TradingAPI { } // All available monney is assigned to this unlimited order - account.assets_avail.put(currency, 0.0); + account.assets_bound.put(currency, 0.0); // we "mis"use order_limit to memorize occupied ammount \ // of currency order_limit = avail; @@ -501,7 +493,7 @@ class TradingEngine implements TradingAPI { // not enough items of asset (shares) available // return null; } - account.assets_avail.put(asset, avail - v); + account.assets_bound.put(asset, avail - v); order_limit = l; break;