xkcd.com
KUPIĘ | SPRZEDAM |
---|---|
45,00 | 41,30 |
42,40 41,50 |
42,50 43,80 |
tick: 41,30
AKA "nie ma sensu się przejmować"
@contextmanager
def TemporaryTable(cursor, model, *args):
columns = ",".join(filter(partial(str.format, '"{}"'), args))
db_table = model._meta.db_table
cursor.execute(f"""
CREATE TEMPORARY TABLE "{db_table}_import"
ON COMMIT DROP
AS SELECT {columns}
FROM "{db_table}"
WHERE NULL"""
)
with TemporaryTable(
cursor, Quote,
'NULL::text AS short_name', 'NULL::date AS date',
'open', 'high', 'low', 'close', 'volume') as quote_import
quotes = StringIO()
for q in data:
date = datetime.strptime(q.date, '%Y%m%d').strftime('%Y-%m-%d')
volume = q.volume.replace('.', '')
quotes.write("\t".join([q.short_name, date,
q.open, q.high, q.low, q.close,
volume]) + "\n")
quotes.seek(0)
cursor.copy_from(
quotes, quote_import,
columns=['short_name', 'date',
'open', 'high', 'low', 'close',
'volume'])
cursor.execute("SELECT count(*) FROM quotes_quote_fill_gaps()")
cursor.fetchone()
create or replace function quotes_quote_fill_gaps()
returns setof integer as $$
with
/* SQL window query */
returning id;
$$ language sql;
cursor.execute(f"""
INSERT INTO "{db_table}"(
"stock_id", "session_id",
"open", "high", "low", "close", "volume"
)
SELECT
"stock"."id", "session"."id",
"open", "high", "low", "close", "volume"
FROM "{db_table}_import"
LEFT JOIN "{stock_table}" AS "stock"
ON "stock"."short_name" = "{db_table}_import"."short_name"
LEFT JOIN "{session_table}" AS "session"
ON "session"."date" = "{db_table}_import"."date"
ON CONFLICT("stock_id", "session_id") DO NOTHING
""")
let ohlc = [
{% for quote in original.quote_set.all %}
[
{{ quote.session.timestamp }} * 1000,
{{ quote.open }},
{{ quote.high }},
{{ quote.low }},
{{ quote.close }}
]{% if not forloop.last %},{% endif %}
{% endfor %}
],
volume = [
{% for quote in original.quote_set.all %}
[
{{ quote.session.timestamp }} * 1000,
{{ quote.volume }},
]{% if not forloop.last %},{% endif %}
{% endfor %}
],
xkcd.com
def rate_of_return(period):
current = F('close')
previous = F('close%i' % period)
return Round((current - previous) / previous, 4)
qs = Quote.objects.annotate(
# add columns with prices shifted by period
**{'close%i' % i: lag(F('close'), i)
for i in return_periods}
).annotate(
# to build JSON on the db side,
# both keys and values must be strings
returns=JSONObject(
keys=Array(*(Text(Value(i))
for i in return_periods)),
values=Array(*(Text(rate_of_return(i))
for i in return_periods)),
),
)
Analogicznie jak w TCP, socket to "końcówka" połączenia
ZeroMQ automagicznie wznawia połączenia
Różne transporty: inproc, ipc, tcp
Różne rodzaje: REQ/REP, PUB/SUB
Wiadomości składają się z jednej lub więcej ramek
Znaczenie ramek zależy od rodzaju socketa
Serwer wystawia socket REP pod jakimś znanym adresem
Klient tworzy socket REQ i łączy się na znany adres
Tylko jeden klient na raz - pozostali czekają
Jeden serwer, wielu klientów
Jeden klient, wiele serwerów
Serwer wystawia socket PUB pod jakimś znanym adresem
Klienci tworzą sockety SUB i łączą się na znany adres
Klienci subskrybują klucze
Serwer wysyła klucz jako pierwszą ramkę wiadomości
PUB
SUB
REP
REQ
opublikuj sesje
od 2022-11-01
do 2022-11-04
ok, 3 sesje
PUB
SUB
ROUTER
nowe bilanse
REQ
poprzednie, poproszę
stare bilanse
połącz
pokaż
JSMQ Dealer
websocket
<TYPE>
ws://mymoney/
<NAME>/<TYPE>
ZWS
<NAME>
ZeroMQ
function CloneClient(state, updates, callback) {
let queued = [],
subscriber = new JSMQ.Subscriber(),
dealer = new JSMQ.Dealer();
dealer.sendReady = () => dealer.send(new JSMQ.Message(state.req));
dealer.onMessage = (items) => {
items.forEach(callback);
queued.forEach(callback);
dealer.disconnect();
subscriber.onMessage = callback; // update dynamically
};
// request state AFTER subscribe
subscriber.sendReady = () => dealer.connect(state.url);
subscriber.onMessage = queued.push;
subscriber.connect(updates.url);
subscriber.subscribe(updates.key);
};
<script type="text/html" id="summary-widget">
<div class="brief">
<h1>
<span title="returns" data-bind="text: ror"/>
<small title="growth" data-bind="text: cagr"/>
</h1>
<span title="total" data-bind="text: total"/>
<span title="stock" data-bind="text: stock"/>
<span title="cash" data-bind="text: cash"/>
</div>
</script>
<!--
ko template:
{ name: 'summary-widget', data: summary }
-->
<!-- /ko -->
function PortfolioSummaryViewModel() {
this.ror = ko.observable('');
this.cagr = ko.observable('');
this.total = ko.observable('');
this.stock = ko.observable('');
this.cash = ko.observable('');
}
var summary = new PortfolioSummaryViewModel();
function onMessage(status) {
summary.ror(status.ror);
...
}
(function(ko) {
ko.applyBindings(
new PortfolioSummaryViewModel(),
$('#suit-center')[0]
);
}(ko));
xkcd.com