Skip to content

Commit

Permalink
Added submit new order for any market to +portfolios.
Browse files Browse the repository at this point in the history
████ ███  To request new features or in case this commit breaks something for you,
████ ███  please, create a new github issue with all possible information for me,
▓███▀█▄   but never share your API Keys!
▒▓██ ███
░▒▓█ ███  Signed-off-by: Carles Tubio <[email protected]>
 _________________________________________
/ Hello, WORLD!                           \
|                                         |
| pssst.. 1.00000000 BTC = 62285.67 EUR.  |
| ======================================= |
| ======================================= |
| = All tests passed (42 assertions in 1  |
\ test case)                              /
 -----------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
  • Loading branch information
ctubio committed Oct 16, 2024
1 parent 576de33 commit 4eefb38
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 57 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ K ?= K.sh
MAJOR = 0
MINOR = 7
PATCH = 0
BUILD = 37
BUILD = 38

OBLIGATORY = DISCLAIMER: This is strict non-violent software: \n$\
if you hurt other living creatures, please stop; \n$\
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ to control a fully configurable high frequency trading engine, with all features
</details>

<details><summary><b>K-+portfolios</b> <sup>(web UI + CLI)</sup></summary>
to show all balances and orders from one exchange, with buttons to edit or cancel orders and links to go to markets:<br />
to show all balances from one exchange, with buttons to create, edit or cancel orders and links to go to markets:<br />

![+portfolios UI Preview](https://github.com/user-attachments/assets/3dd3488a-a466-4817-89f7-a6d581ddac9e)
![+portfolios UI Preview](https://github.com/user-attachments/assets/e0e3ce7d-3388-45a9-a559-4ba239a9e880)
</details>

<details><summary><b>K-hello-world</b> <sup>(CLI)</sup></summary>
Expand Down
39 changes: 24 additions & 15 deletions src/bin/+portfolios/+portfolios.client/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,29 @@ import {Socket, Models} from 'lib/K';
@Component({
selector: 'client',
template: `<div class="row">
<div class="col-md-12 col-xs-12">
<div class="col-md-6">
<settings
[product]="product"
[settings]="settings"></settings>
<wallets
[wallets]="wallets"
[markets]="markets"
[settings]="settings"></wallets>
</div>
<div class="col-md-6">
<orders
[orders]="orders"
[markets]="markets"></orders>
</div>
<div class="col-md-12 col-xs-12" [hidden]="!showSubmitOrder">
<submit-order
[markets]="markets"></submit-order>
</div>
<div class="col-md-12 col-xs-12">
<div class="col-md-6">
<button type="button"
class="btn btn-default" style="position: absolute; top: 10px;"
(click)="showSubmitOrder = !showSubmitOrder">New Order</button>
<settings
[product]="product"
[settings]="settings"></settings>
<wallets
[wallets]="wallets"
[markets]="markets"
[settings]="settings"></wallets>
</div>
<div class="col-md-6">
<orders
[orders]="orders"
[markets]="markets"></orders>
</div>
</div>
</div>`
})
export class ClientComponent implements OnInit {
Expand All @@ -33,6 +40,8 @@ export class ClientComponent implements OnInit {

private settings: Models.PortfolioParameters = new Models.PortfolioParameters();

private showSubmitOrder: boolean = false;

@Input() addr: string;

@Input() tradeFreq: number;
Expand Down
8 changes: 4 additions & 4 deletions src/bin/+portfolios/+portfolios.client/Orders.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Component, Input} from '@angular/core';

import {GridOptions, GridApi, CellValueChangedEvent, INumberCellEditorParams} from 'ag-grid-community';
import {GridOptions, GridApi} from 'ag-grid-community';

import {Shared, Socket, Models} from 'lib/K';

Expand Down Expand Up @@ -122,7 +122,7 @@ export class OrdersComponent {
min: parseFloat(Math.pow(10, -params.data.pricePrecision).toFixed(params.data.pricePrecision)),
step: parseFloat(Math.pow(10, -params.data.pricePrecision).toFixed(params.data.pricePrecision)),
showStepperButtons: true
} as INumberCellEditorParams };
} };
},
cellRenderer: (params) => '<span style="display: inline-block;">&#9998;</span>' + params.value.toFixed(params.data.pricePrecision),
cellClassRules: {
Expand Down Expand Up @@ -181,12 +181,12 @@ export class OrdersComponent {
this.fireCxl.fire(new Models.OrderCancelRequestFromUI($event.data.orderId, $event.data.exchange));
};

private onCellValueChanged = ($event: CellValueChangedEvent) => {
private onCellValueChanged = ($event) => {
this.editCxl.fire(new Models.OrderEditRequestFromUI(
$event.data.orderId,
parseFloat($event.data.price),
$event.data.quantity,
$event.data.side,
+($event.data.side == "Ask"),
$event.data.symbol
));
};
Expand Down
117 changes: 117 additions & 0 deletions src/bin/+portfolios/+portfolios.client/Submit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import {Component, Input} from '@angular/core';

import {Socket, Models} from 'lib/K';

@Component({
selector: 'submit-order',
template: `<table class="table table-responsive">
<thead>
<tr>
<th style="width:100px;">Symbol:</th>
<th style="width:100px;">Side:</th>
<th style="width:100px;">Price:</th>
<th style="width:100px;">Size:</th>
<th style="width:100px;">TIF:</th>
<th style="width:100px;">Type:</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<select class="form-control input-sm" [(ngModel)]="symbol">
<option
*ngFor="let sym of availableSymbols"
[ngValue]="sym"
>{{ sym }}</option>
</select>
</td>
<td>
<select id="selectSide" class="form-control input-sm" [(ngModel)]="side">
<option
*ngFor="let option of availableSides"
[ngValue]="option.val"
>{{ option.str }}</option>
</select>
</td>
<td>
<input
id="orderPriceInput"
class="form-control input-sm"
type="number"
min="0"
[(ngModel)]="price" />
</td>
<td>
<input
id="orderSizeInput"
class="form-control input-sm"
type="number"
min="0"
[(ngModel)]="quantity" />
</td>
<td>
<select class="form-control input-sm" [(ngModel)]="timeInForce">
<option
*ngFor="let option of availableTifs"
[ngValue]="option.val"
>{{ option.str }}</option>
</select>
</td>
<td>
<select class="form-control input-sm" [(ngModel)]="type">
<option
*ngFor="let option of availableOrderTypes"
[ngValue]="option.val"
>{{ option.str }}</option>
</select>
</td>
<td style="width:70px;" rowspan="2" class="text-center">
<button
type="button"
class="btn btn-default"
(click)="submitManualOrder()"
>Submit</button>
</td>
</tr>
</tbody>
</table>`
})
export class SubmitComponent {

private symbol: string;
private side: Models.Side = Models.Side.Bid;
private price: number;
private quantity: number;
private timeInForce: Models.TimeInForce = Models.TimeInForce.GTC;
private type: Models.OrderType = Models.OrderType.Limit;

private availableSides: Models.Map[] = Models.getMap(Models.Side);
private availableTifs: Models.Map[] = Models.getMap(Models.TimeInForce);
private availableOrderTypes: Models.Map[] = Models.getMap(Models.OrderType);
private availableSymbols: string[] = [];

private fireCxl: Socket.IFire<Models.OrderRequestFromUI> = new Socket.Fire(Models.Topics.SubmitNewOrder);


@Input() set markets(o: any) {
if (!this.availableSymbols.length)
for (let x in o)
for (let z in o[x])
if (!this.availableSymbols.filter(s => s == o[x][z].symbol).length) {
this.availableSymbols.push(o[x][z].symbol);
this.availableSymbols.sort();
}
};

private submitManualOrder = () => {
if (this.price && this.quantity)
this.fireCxl.fire(new Models.OrderRequestFromUI(
this.symbol,
this.side,
this.price,
this.quantity,
this.timeInForce,
this.type
));
};
};
61 changes: 38 additions & 23 deletions src/bin/+portfolios/+portfolios.data.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,30 @@ namespace analpaper {
j = k.to_json();
};

struct ButtonSubmitNewOrder: public Client::Clickable {
private_ref:
const KryptoNinja &K;
public:
ButtonSubmitNewOrder(const KryptoNinja &bot)
: Clickable(bot)
, K(bot)
{};
void click(const json &j) override {
if (j.is_object()
and !j.value("symbol", "").empty()
and j.value("price", 0.0)
and j.value("quantity", 0.0)
) {
json order = j;
order["manual"] = true;
order["orderId"] = K.gateway->randId();
K.clicked(this, order);
}
};
mMatter about() const override {
return mMatter::SubmitNewOrder;
};
};
struct ButtonCancelOrder: public Client::Clickable {
private_ref:
const KryptoNinja &K;
Expand All @@ -393,42 +417,33 @@ namespace analpaper {
};
struct InputEditOrder: public Client::Clickable {
private_ref:
const KryptoNinja &K;
ButtonCancelOrder &cancel;
const KryptoNinja &K;
ButtonSubmitNewOrder &submit;
ButtonCancelOrder &cancel;
public:
InputEditOrder(const KryptoNinja &bot, ButtonCancelOrder &c)
InputEditOrder(const KryptoNinja &bot, ButtonSubmitNewOrder &s, ButtonCancelOrder &c)
: Clickable(bot)
, K(bot)
, submit(s)
, cancel(c)
{};
void click(const json &j) override {
cancel.click(j);
if (j.is_object()
and !j.value("symbol", "").empty()
and !j.value("side", "").empty()
and j.value("price", 0.0)
and j.value("quantity", 0.0)
) K.clicked(this, Order(
j.value("symbol", ""),
j.value("side", "") == "Bid" ? Side::Bid : Side::Ask,
j.value("price", 0.0),
j.value("quantity", 0.0),
Tstamp,
false,
K.gateway->randId()
));
submit.click(j);
};
mMatter about() const override {
return mMatter::EditOrder;
};
};

struct Buttons {
ButtonCancelOrder cancel;
InputEditOrder edit;
ButtonSubmitNewOrder submit;
ButtonCancelOrder cancel;
InputEditOrder edit;
Buttons(const KryptoNinja &bot)
: cancel(bot)
, edit(bot, cancel)
: submit(bot)
, cancel(bot)
, edit(bot, submit, cancel)
{};
};

Expand All @@ -440,8 +455,8 @@ namespace analpaper {
public:
Broker(const KryptoNinja &bot, const Buttons &b)
: Clicked(bot, {
{&b.cancel, [&](const json &j) { K.cancel(j); }},
{&b.edit, [&](const Order &o) { K.place(o); }}
{&b.submit, [&](const json &j) { K.place(j); }},
{&b.cancel, [&](const json &j) { K.cancel(j); }}
})
, memory(bot)
, semaphore(bot)
Expand Down
2 changes: 0 additions & 2 deletions src/bin/trading-bot/trading-bot.client/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import {Socket, Shared, Models} from 'lib/K';
<div>
<button type="button"
class="btn btn-default"
id="order_form"
(click)="showSubmitOrder = !showSubmitOrder">Submit Order</button>
</div>
<div style="padding-top: 2px;padding-bottom: 2px;">
Expand Down Expand Up @@ -71,7 +70,6 @@ import {Socket, Shared, Models} from 'lib/K';
<button type="button"
class="btn btn-default"
style="margin:14px 0px;"
id="order_form"
(click)="toggleSettings()">Settings
</button>
</div>
Expand Down
9 changes: 7 additions & 2 deletions src/bin/trading-bot/trading-bot.client/Submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {Socket, Models} from 'lib/K';
class="form-control input-sm"
type="number"
step="{{ product.stepSize }}"
min="{{ product.minSize}}"
min="{{ product.minSize }}"
[(ngModel)]="quantity" />
</td>
<td>
Expand Down Expand Up @@ -88,7 +88,12 @@ export class SubmitComponent {
private submitManualOrder = () => {
if (this.price && this.quantity)
this.fireCxl.fire(new Models.OrderRequestFromUI(
this.side, this.price, this.quantity, this.timeInForce, this.type
this.product.symbol,
this.side,
this.price,
this.quantity,
this.timeInForce,
this.type
));
};
};
7 changes: 5 additions & 2 deletions src/bin/trading-bot/trading-bot.data.h
Original file line number Diff line number Diff line change
Expand Up @@ -995,10 +995,13 @@ namespace tribeca {
, K(bot)
{};
void click(const json &j) override {
if (j.is_object() and j.value("price", 0.0) and j.value("quantity", 0.0)) {
if (j.is_object()
and !j.value("symbol", "").empty()
and j.value("price", 0.0)
and j.value("quantity", 0.0)
) {
json order = j;
order["manual"] = true;
order["symbol"] = K.gateway->symbol;
order["orderId"] = K.gateway->randId();
K.clicked(this, order);
}
Expand Down
10 changes: 5 additions & 5 deletions src/bin/trading-bot/trading-bot.test.h
Original file line number Diff line number Diff line change
Expand Up @@ -444,11 +444,11 @@ SCENARIO_METHOD(TradingBot, "ANY BTC/EUR") {
THEN("to json") {
REQUIRE(string::npos == engine.orders.blob().dump().find("\"status\":0"));
REQUIRE(string::npos == engine.orders.blob().dump().find("\"status\":2"));
REQUIRE(string::npos != engine.orders.blob().dump().find("{\"exchangeId\":\"\",\"isPong\":false,\"latency\":69,\"orderId\":\"" + randIds[0] + "\",\"price\":1234.5,\"quantity\":0.12345678,\"side\":0,\"status\":1,\"symbol\":\"BTC-EUR\",\"time\":" + to_string(time) + ",\"timeInForce\":0,\"type\":0}"));
REQUIRE(string::npos != engine.orders.blob().dump().find("{\"exchangeId\":\"\",\"isPong\":false,\"latency\":69,\"orderId\":\"" + randIds[1] + "\",\"price\":1234.51,\"quantity\":0.12345679,\"side\":0,\"status\":1,\"symbol\":\"BTC-EUR\",\"time\":" + to_string(time) + ",\"timeInForce\":0,\"type\":0}"));
REQUIRE(string::npos != engine.orders.blob().dump().find("{\"exchangeId\":\"\",\"isPong\":false,\"latency\":69,\"orderId\":\"" + randIds[2] + "\",\"price\":1234.52,\"quantity\":0.1234568,\"side\":0,\"status\":1,\"symbol\":\"BTC-EUR\",\"time\":" + to_string(time) + ",\"timeInForce\":0,\"type\":0}"));
REQUIRE(string::npos != engine.orders.blob().dump().find("{\"exchangeId\":\"\",\"isPong\":false,\"latency\":69,\"orderId\":\"" + randIds[3] + "\",\"price\":1234.5,\"quantity\":0.12345678,\"side\":1,\"status\":1,\"symbol\":\"BTC-EUR\",\"time\":" + to_string(time) + ",\"timeInForce\":0,\"type\":0}"));
REQUIRE(string::npos != engine.orders.blob().dump().find("{\"exchangeId\":\"\",\"isPong\":false,\"latency\":69,\"orderId\":\"" + randIds[4] + "\",\"price\":1234.51,\"quantity\":0.12345679,\"side\":1,\"status\":1,\"symbol\":\"BTC-EUR\",\"time\":" + to_string(time) + ",\"timeInForce\":0,\"type\":0}"));
REQUIRE(string::npos != engine.orders.blob().dump().find("{\"exchangeId\":\"\",\"isPong\":false,\"latency\":69,\"orderId\":\"" + randIds[0] + "\",\"price\":1234.5,\"pricePrecision\":0.0,\"quantity\":0.12345678,\"quantityPrecision\":0.0,\"side\":0,\"status\":1,\"symbol\":\"BTC-EUR\",\"time\":" + to_string(time) + ",\"timeInForce\":0,\"type\":0}"));
REQUIRE(string::npos != engine.orders.blob().dump().find("{\"exchangeId\":\"\",\"isPong\":false,\"latency\":69,\"orderId\":\"" + randIds[1] + "\",\"price\":1234.51,\"pricePrecision\":0.0,\"quantity\":0.12345679,\"quantityPrecision\":0.0,\"side\":0,\"status\":1,\"symbol\":\"BTC-EUR\",\"time\":" + to_string(time) + ",\"timeInForce\":0,\"type\":0}"));
REQUIRE(string::npos != engine.orders.blob().dump().find("{\"exchangeId\":\"\",\"isPong\":false,\"latency\":69,\"orderId\":\"" + randIds[2] + "\",\"price\":1234.52,\"pricePrecision\":0.0,\"quantity\":0.1234568,\"quantityPrecision\":0.0,\"side\":0,\"status\":1,\"symbol\":\"BTC-EUR\",\"time\":" + to_string(time) + ",\"timeInForce\":0,\"type\":0}"));
REQUIRE(string::npos != engine.orders.blob().dump().find("{\"exchangeId\":\"\",\"isPong\":false,\"latency\":69,\"orderId\":\"" + randIds[3] + "\",\"price\":1234.5,\"pricePrecision\":0.0,\"quantity\":0.12345678,\"quantityPrecision\":0.0,\"side\":1,\"status\":1,\"symbol\":\"BTC-EUR\",\"time\":" + to_string(time) + ",\"timeInForce\":0,\"type\":0}"));
REQUIRE(string::npos != engine.orders.blob().dump().find("{\"exchangeId\":\"\",\"isPong\":false,\"latency\":69,\"orderId\":\"" + randIds[4] + "\",\"price\":1234.51,\"pricePrecision\":0.0,\"quantity\":0.12345679,\"quantityPrecision\":0.0,\"side\":1,\"status\":1,\"symbol\":\"BTC-EUR\",\"time\":" + to_string(time) + ",\"timeInForce\":0,\"type\":0}"));
}
}
WHEN("ready") {
Expand Down
Loading

0 comments on commit 4eefb38

Please sign in to comment.