raylu преди 2 седмици
родител
ревизия
dae77d0791
променени са 4 файла, в които са добавени 49 реда и са изтрити 5 реда
  1. 18 1
      buy.py
  2. 25 3
      ts/buy.ts
  3. 2 0
      www/buy.html
  4. 4 1
      www/style.css

+ 18 - 1
buy.py

@@ -3,6 +3,8 @@ from __future__ import annotations
 import collections
 import concurrent.futures
 import dataclasses
+import json
+import math
 import sys
 import typing
 
@@ -28,6 +30,7 @@ def main() -> None:
 
 	# what's left to buy
 	materials: list[Material] = []
+	price_limits: dict[str, int] = {}
 	for mat, amount in buy.items():
 		remaining = max(amount - bids[mat] - warehouse.get(mat, 0) - exports.get(mat, 0), 0)
 		price = raw_prices[mat]
@@ -37,8 +40,11 @@ def main() -> None:
 		spread = price['Ask'] - price['Bid']
 		materials.append(Material(mat, amount=amount, bids=bids[mat], have=warehouse.get(mat, 0) + exports.get(mat, 0),
 				spread=spread, savings=spread * remaining))
+		epsilon = 10 ** (int(math.log10(price['Bid'])) - 2)
+		price_limits[mat] = price['Bid'] + 2 * epsilon
 	materials.sort(reverse=True)
 
+	to_buy: dict[str, int] = {}
 	print('mat   want  bids  have   buy  savings')
 	for m in materials:
 		buy = max(m.amount - m.bids - m.have, 0)
@@ -47,6 +53,17 @@ def main() -> None:
 		else:
 			bids = str(m.bids).rjust(5)
 		print(f'{m.ticker:4} {m.amount:>5} {bids} {m.have:>5} {buy:>5} {m.savings:8.0f}')
+		if buy > 0:
+			to_buy[m.ticker] = buy
+
+	print('\n' + json.dumps({
+		'actions': [
+			{'name': 'BuyItems', 'type': 'CX Buy', 'group': 'A1', 'exchange': 'IC1',
+				'priceLimits': price_limits, 'buyPartial': True, 'allowUnfilled': True, 'useCXInv': False},
+		],
+		'global': {'name': f'buy orders for {days} days'},
+		'groups': [{'type': 'Manual', 'name': 'A1', 'materials': to_buy}],
+	}))
 
 	# deposits of current bids
 	orders.sort(key=lambda order: order['Limit'] * order['Amount'], reverse=True)
@@ -99,7 +116,7 @@ def get_bids() -> tuple[typing.Mapping[str, int], list[market.ExchangeOrder]]:
 		bids[order['MaterialTicker']] += order['Amount']
 	return bids, orders
 
-@dataclasses.dataclass(eq=False, slots=True)
+@dataclasses.dataclass(eq=False, frozen=True, slots=True)
 class Material:
 	ticker: str
 	amount: int

+ 25 - 3
ts/buy.ts

@@ -10,6 +10,7 @@ const apiKey = document.querySelector('#api-key') as HTMLInputElement;
 	if (storedApiKey)
 		apiKey.value = storedApiKey;
 }
+const xitAct = document.querySelector('textarea#xit-act') as HTMLTextAreaElement;
 document.querySelector('#fetch')!.addEventListener('click', async () => {
 	const supplyForDays = parseInt((document.querySelector('#days') as HTMLInputElement).value, 10);
 	const cx = (document.querySelector('#cx') as HTMLInputElement).value;
@@ -18,6 +19,9 @@ document.querySelector('#fetch')!.addEventListener('click', async () => {
 	localStorage.setItem('fio-username', username.value);
 	localStorage.setItem('fio-api-key', apiKey.value);
 });
+document.querySelector('#copy-xit-act')!.addEventListener('click', () => {
+	navigator.clipboard.writeText(xitAct.value);
+});
 setupPopover();
 
 async function calculate(username: string, apiKey: string, supplyForDays: number, cx: string): Promise<void> {
@@ -59,9 +63,11 @@ async function calculate(username: string, apiKey: string, supplyForDays: number
 	}
 	materials.sort((a, b) => b.savings - a.savings);
 
+	const toBuy: Record<string, number> = {};
+	const priceLimits: Record<string, number> = {};
 	const tbody = document.querySelector('tbody')!;
 	tbody.innerHTML = '';
-	const format= new Intl.NumberFormat(undefined, {maximumFractionDigits: 0}).format;
+	const format = new Intl.NumberFormat(undefined, {maximumFractionDigits: 0}).format;
 	for (const m of materials) {
 		const tr = document.createElement('tr');
 		const buyAmount = Math.max(m.amount - m.bids - m.have, 0);
@@ -74,11 +80,27 @@ async function calculate(username: string, apiKey: string, supplyForDays: number
 			<td>${format(m.spread)}</td>
 			<td>${format(m.savings)}</td>
 		`;
-		if (m.bids === 0 && buyAmount > 0)
-			tr.children[2].classList.add('red');
+		if (buyAmount > 0) {
+			if (m.bids === 0)
+				tr.children[2].classList.add('red');
+			toBuy[m.ticker] = buyAmount;
+			const bid = prices.get(m.ticker)!.Bid!;
+			const epsilon = 10 ** (Math.floor(Math.log10(bid)) - 2);
+			const limit = bid + 2 * epsilon;
+			priceLimits[m.ticker] = limit;
+		}
 		tbody.appendChild(tr);
 	}
 
+	xitAct.value = JSON.stringify({
+		'actions': [
+			{'name': 'BuyItems', 'type': 'CX Buy', 'group': 'A1', 'exchange': 'IC1',
+				'priceLimits': priceLimits, 'buyPartial': true, 'allowUnfilled': true, 'useCXInv': false},
+		],
+		'global': {'name': `buy orders for ${supplyForDays} days`},
+		'groups': [{'type': 'Manual', 'name': 'A1', 'materials': toBuy}],
+	});
+
 	// deposits of current bids
 	orders.sort((a, b) => (b.Limit * b.Amount) - (a.Limit * a.Amount));
 	for (const order of orders) {

+ 2 - 0
www/buy.html

@@ -37,6 +37,8 @@
 			</thead>
 			<tbody></tbody>
 		</table>
+		<textarea id="xit-act" readonly></textarea>
+		<input type="button" value="copy" id="copy-xit-act">
 	</main>
 	<div id="popover" popover="hint"></div>
 	<script src="buy.js"></script>

+ 4 - 1
www/style.css

@@ -33,7 +33,7 @@ main.roi form {
 	display: flex;
 	justify-content: space-evenly;
 }
-input, select {
+input, select, textarea {
 	background-color: #171120;
 	color: inherit;
 	font-family: inherit;
@@ -43,6 +43,9 @@ input, select {
 input[type="checkbox"] {
 	accent-color: #f70;
 }
+main.buy textarea {
+	margin-top: 2em;
+}
 
 main, footer {
 	width: 900px;