Add new securities and use real prices in FIX UI order entry
Browse files- securities.txt: add EUROB, AEG, INTKA, AAAK, ATTIK to existing symbols
- fix-ui-client: load securities as {symbol: price} dict so prices flow through
- index.html: auto-populate price field from current security price on symbol change
- oeg_simulator: read symbols and prices from securities.txt instead of hardcoded FOO/AAA/BBB at ~100
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fix-ui-client/fix-ui-client.py +7 -3
- fix-ui-client/templates/index.html +13 -2
- oeg/oeg_simulator.py +31 -8
- shared_data/securities.txt +9 -4
fix-ui-client/fix-ui-client.py
CHANGED
|
@@ -145,16 +145,20 @@ def stop_fix():
|
|
| 145 |
fix_initiator = None
|
| 146 |
|
| 147 |
def load_securities():
|
| 148 |
-
|
| 149 |
try:
|
| 150 |
with open(Config.SECURITIES_FILE) as f:
|
| 151 |
for line in f:
|
| 152 |
line = line.strip()
|
| 153 |
if line and not line.startswith('#'):
|
| 154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
except Exception:
|
| 156 |
pass
|
| 157 |
-
return
|
| 158 |
|
| 159 |
# --- Flask routes ---
|
| 160 |
@app.route("/")
|
|
|
|
| 145 |
fix_initiator = None
|
| 146 |
|
| 147 |
def load_securities():
|
| 148 |
+
securities = {}
|
| 149 |
try:
|
| 150 |
with open(Config.SECURITIES_FILE) as f:
|
| 151 |
for line in f:
|
| 152 |
line = line.strip()
|
| 153 |
if line and not line.startswith('#'):
|
| 154 |
+
parts = line.split()
|
| 155 |
+
if len(parts) >= 3:
|
| 156 |
+
securities[parts[0]] = float(parts[2])
|
| 157 |
+
elif len(parts) >= 1:
|
| 158 |
+
securities[parts[0]] = 10.0
|
| 159 |
except Exception:
|
| 160 |
pass
|
| 161 |
+
return securities or {"ALPHA": 5.65, "PEIR": 8.35, "EXAE": 6.90, "QUEST": 13.35, "NBG": 8.00}
|
| 162 |
|
| 163 |
# --- Flask routes ---
|
| 164 |
@app.route("/")
|
fix-ui-client/templates/index.html
CHANGED
|
@@ -158,7 +158,7 @@
|
|
| 158 |
</div>
|
| 159 |
<div class="form-group">
|
| 160 |
<label>Symbol</label>
|
| 161 |
-
<select id="f-symbol">
|
| 162 |
{% for s in securities %}
|
| 163 |
<option value="{{ s }}">{{ s }}</option>
|
| 164 |
{% endfor %}
|
|
@@ -170,7 +170,7 @@
|
|
| 170 |
</div>
|
| 171 |
<div class="form-group">
|
| 172 |
<label>Price</label>
|
| 173 |
-
<input id="f-price" type="number" step="0.01" value="
|
| 174 |
</div>
|
| 175 |
<button type="submit" class="btn-send">Send Order</button>
|
| 176 |
</form>
|
|
@@ -186,6 +186,16 @@
|
|
| 186 |
</div>
|
| 187 |
|
| 188 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
// Works whether served at / or under /fix/
|
| 190 |
const BASE = window.location.pathname === '/' ? ''
|
| 191 |
: window.location.pathname.replace(/\/$/, '');
|
|
@@ -264,6 +274,7 @@
|
|
| 264 |
pollMessages();
|
| 265 |
setInterval(pollStatus, 2000);
|
| 266 |
setInterval(pollMessages, 2000);
|
|
|
|
| 267 |
</script>
|
| 268 |
|
| 269 |
</body>
|
|
|
|
| 158 |
</div>
|
| 159 |
<div class="form-group">
|
| 160 |
<label>Symbol</label>
|
| 161 |
+
<select id="f-symbol" onchange="updatePrice()">
|
| 162 |
{% for s in securities %}
|
| 163 |
<option value="{{ s }}">{{ s }}</option>
|
| 164 |
{% endfor %}
|
|
|
|
| 170 |
</div>
|
| 171 |
<div class="form-group">
|
| 172 |
<label>Price</label>
|
| 173 |
+
<input id="f-price" type="number" step="0.01" value="">
|
| 174 |
</div>
|
| 175 |
<button type="submit" class="btn-send">Send Order</button>
|
| 176 |
</form>
|
|
|
|
| 186 |
</div>
|
| 187 |
|
| 188 |
<script>
|
| 189 |
+
const SECURITIES_PRICES = {{ securities | tojson }};
|
| 190 |
+
|
| 191 |
+
function updatePrice() {
|
| 192 |
+
const sym = document.getElementById('f-symbol').value;
|
| 193 |
+
const price = SECURITIES_PRICES[sym];
|
| 194 |
+
if (price !== undefined) {
|
| 195 |
+
document.getElementById('f-price').value = price.toFixed(2);
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
// Works whether served at / or under /fix/
|
| 200 |
const BASE = window.location.pathname === '/' ? ''
|
| 201 |
: window.location.pathname.replace(/\/$/, '');
|
|
|
|
| 274 |
pollMessages();
|
| 275 |
setInterval(pollStatus, 2000);
|
| 276 |
setInterval(pollMessages, 2000);
|
| 277 |
+
updatePrice();
|
| 278 |
</script>
|
| 279 |
|
| 280 |
</body>
|
oeg/oeg_simulator.py
CHANGED
|
@@ -1,25 +1,48 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
import time, json, random, requests, os
|
|
|
|
| 3 |
FRONTEND = os.environ.get("FRONTEND_URL", "http://frontend:5000")
|
| 4 |
-
|
| 5 |
-
SIDES = ["buy","sell"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
def post_order(order):
|
| 8 |
try:
|
| 9 |
r = requests.post(FRONTEND + "/submit", json=order, timeout=5)
|
| 10 |
-
print(json.dumps({"component":"oeg","event":"post_order","payload":{"order":order,"status":r.status_code}}))
|
| 11 |
except Exception as e:
|
| 12 |
-
print(json.dumps({"component":"oeg","event":"post_failed","payload":{"order":order,"error":str(e)}}))
|
|
|
|
| 13 |
|
| 14 |
if __name__ == "__main__":
|
|
|
|
|
|
|
|
|
|
| 15 |
try:
|
| 16 |
while True:
|
|
|
|
|
|
|
| 17 |
order = {
|
| 18 |
-
"order_id": str(int(time.time()*1000)),
|
| 19 |
-
"symbol":
|
| 20 |
"type": random.choice(SIDES),
|
| 21 |
-
"quantity": random.choice([5,10,20]),
|
| 22 |
-
"price": round(
|
| 23 |
"timestamp": time.time(),
|
| 24 |
"source": "oeg-sim"
|
| 25 |
}
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
import time, json, random, requests, os
|
| 3 |
+
|
| 4 |
FRONTEND = os.environ.get("FRONTEND_URL", "http://frontend:5000")
|
| 5 |
+
SECURITIES_FILE = os.environ.get("SECURITIES_FILE", "/app/data/securities.txt")
|
| 6 |
+
SIDES = ["buy", "sell"]
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def load_securities():
|
| 10 |
+
securities = {}
|
| 11 |
+
try:
|
| 12 |
+
with open(SECURITIES_FILE) as f:
|
| 13 |
+
for line in f:
|
| 14 |
+
line = line.strip()
|
| 15 |
+
if line and not line.startswith('#'):
|
| 16 |
+
parts = line.split()
|
| 17 |
+
if len(parts) >= 3:
|
| 18 |
+
securities[parts[0]] = float(parts[2])
|
| 19 |
+
except Exception:
|
| 20 |
+
pass
|
| 21 |
+
return securities or {"ALPHA": 5.65, "PEIR": 8.35, "EXAE": 6.90}
|
| 22 |
+
|
| 23 |
|
| 24 |
def post_order(order):
|
| 25 |
try:
|
| 26 |
r = requests.post(FRONTEND + "/submit", json=order, timeout=5)
|
| 27 |
+
print(json.dumps({"component": "oeg", "event": "post_order", "payload": {"order": order, "status": r.status_code}}))
|
| 28 |
except Exception as e:
|
| 29 |
+
print(json.dumps({"component": "oeg", "event": "post_failed", "payload": {"order": order, "error": str(e)}}))
|
| 30 |
+
|
| 31 |
|
| 32 |
if __name__ == "__main__":
|
| 33 |
+
securities = load_securities()
|
| 34 |
+
symbols = list(securities.keys())
|
| 35 |
+
print(json.dumps({"component": "oeg", "event": "loaded_securities", "payload": {"symbols": symbols}}))
|
| 36 |
try:
|
| 37 |
while True:
|
| 38 |
+
symbol = random.choice(symbols)
|
| 39 |
+
base_price = securities[symbol]
|
| 40 |
order = {
|
| 41 |
+
"order_id": str(int(time.time() * 1000)),
|
| 42 |
+
"symbol": symbol,
|
| 43 |
"type": random.choice(SIDES),
|
| 44 |
+
"quantity": random.choice([5, 10, 20]),
|
| 45 |
+
"price": round(base_price + random.uniform(-0.5, 0.5), 2),
|
| 46 |
"timestamp": time.time(),
|
| 47 |
"source": "oeg-sim"
|
| 48 |
}
|
shared_data/securities.txt
CHANGED
|
@@ -1,6 +1,11 @@
|
|
| 1 |
#SYMBOL <start_price> <current_price>
|
| 2 |
-
ALPHA
|
| 3 |
-
PEIR
|
| 4 |
-
EXAE
|
| 5 |
QUEST 12.60 13.35
|
| 6 |
-
NBG
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
#SYMBOL <start_price> <current_price>
|
| 2 |
+
ALPHA 5.95 5.65
|
| 3 |
+
PEIR 8.05 8.35
|
| 4 |
+
EXAE 6.05 6.90
|
| 5 |
QUEST 12.60 13.35
|
| 6 |
+
NBG 8.05 8.00
|
| 7 |
+
EUROB 3.20 3.45
|
| 8 |
+
AEG 4.25 4.75
|
| 9 |
+
INTKA 7.15 7.35
|
| 10 |
+
AAAK 2.25 2.75
|
| 11 |
+
ATTIK 4.15 4.9
|