going somewhere

This commit is contained in:
didier amyot committed 2018-11-12 14:50:56 -05:00
1 parent b972e6e69f
commit 10b054db25
31 files changed
+620 -301

No files matched your search

+1
View File
@@ -39,6 +39,7 @@ file should be self documented.
* [clojure](https://clojure.org/) for programming language
* [kafka](http://kafka.apache.org/) for message broker/database
* [datomic-free](https://www.datomic.com/) for transaction needs
* [clickhouse](https://clickhouse.yandex) to store the market data
#### In the browser
* [clojure script](https://clojurescript.org/) for programming language
+8
View File
@@ -159,6 +159,14 @@ services:
- "4335:4335"
- "4336:4336"
clickhouse:
image: "yandex/clickhouse-server:18.14"
restart: "unless-stopped"
ports:
- "8123:8123"
- "9000:9000"
# skirmish itself
+2 -4
View File
@@ -1,7 +1,6 @@
(ns skirmish.wui.middleware
(:require [ring.middleware.defaults :refer [site-defaults wrap-defaults]]
[prone.middleware :refer [wrap-exceptions]]
[ring.middleware.reload :refer [wrap-reload]]))
[prone.middleware :refer [wrap-exceptions]]))
(defn custom-dev-middleware [handler]
(fn [request]
@@ -12,5 +11,4 @@
(-> handler
(wrap-defaults site-defaults)
custom-dev-middleware
wrap-exceptions
wrap-reload))
wrap-exceptions))
+3 -3
View File
@@ -5,9 +5,9 @@
"requires": true,
"dependencies": {
"bulma": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.1.tgz",
"integrity": "sha512-wRSO2LXB+qI9Pyz2id+uZr4quz5aftSN7Ay1ysr1+krzVp3utD+Ci4CeKuZdrYGc800t65b7heXBL6qw2Wo/lQ=="
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.2.tgz",
"integrity": "sha512-6JHEu8U/1xsyOst/El5ImLcZIiE2JFXgvrz8GGWbnDLwTNRPJzdAM0aoUM1Ns0avALcVb6KZz9NhzmU53dGDcQ=="
},
"font-awesome": {
"version": "4.7.0",
+1 -1
View File
@@ -56,7 +56,7 @@
:java-source-paths ["src/java"]
:npm {:dependencies [[bulma "0.7.1"]
:npm {:dependencies [[bulma "0.7.2"]
[font-awesome "4.7.0"]]}
:ring {:handler skirmish.wui.handler/app
+2 -6
View File
@@ -38,14 +38,10 @@
:ip "0.0.0.0"
:max-body 524288 ;; 512kb, small but enough
:max-ws 524288
:default-sec-id :dapm
:datomic {:transactor-url "datomic:free://localhost:4334/skirmish.broker"}}
:wui
{:port #long #or [#env WUI_PORT 3000]}
;; stupid parameters about benchmarking tools
:benchmark {
:number-of-requests-per-securities 30}}
{:port #long #or [#env WUI_PORT 3000]}}
+381 -54
View File
@@ -14,7 +14,7 @@
-ms-user-select: none;
user-select: none; }
.select:not(.is-multiple):not(.is-loading)::after, .navbar-link::after {
.select:not(.is-multiple):not(.is-loading)::after, .navbar-link:not(.is-arrowless)::after {
border: 3px solid transparent;
border-radius: 2px;
border-right: 0;
@@ -31,7 +31,7 @@
width: 0.625em; }
.box:not(:last-child), .content:not(:last-child), .notification:not(:last-child), .progress:not(:last-child), .table:not(:last-child), .table-container:not(:last-child), .title:not(:last-child),
.subtitle:not(:last-child), .block:not(:last-child), .highlight:not(:last-child), .breadcrumb:not(:last-child), .level:not(:last-child), .message:not(:last-child), .tabs:not(:last-child) {
.subtitle:not(:last-child), .block:not(:last-child), .highlight:not(:last-child), .breadcrumb:not(:last-child), .level:not(:last-child), .list:not(:last-child), .message:not(:last-child), .tabs:not(:last-child) {
margin-bottom: 1.5rem; }
.delete, .modal-close {
@@ -41,6 +41,7 @@
border: none;
border-radius: 290486px;
cursor: pointer;
pointer-events: auto;
display: inline-block;
flex-grow: 0;
flex-shrink: 0;
@@ -993,6 +994,16 @@ a.has-text-danger:hover, a.has-text-danger:focus {
.is-hidden {
display: none !important; }
.is-sr-only {
border: none !important;
clip: rect(0, 0, 0, 0) !important;
height: 0.01em !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
white-space: nowrap !important;
width: 0.01em !important; }
@media screen and (max-width: 768px) {
.is-hidden-mobile {
display: none !important; } }
@@ -1081,10 +1092,10 @@ a.has-text-danger:hover, a.has-text-danger:focus {
box-shadow: none !important; }
.box {
background-color: white;
background-color: #7a7a7a;
border-radius: 6px;
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
color: whitesmoke;
box-shadow: 0 2px 3px #b5b5b5, 0 0 0 1px #b5b5b5;
color: white;
display: block;
padding: 1.25rem; }
@@ -1788,7 +1799,7 @@ a.box:active {
justify-content: flex-start; }
.buttons .button {
margin-bottom: 0.5rem; }
.buttons .button:not(:last-child) {
.buttons .button:not(:last-child):not(.is-fullwidth) {
margin-right: 0.5rem; }
.buttons:last-child {
margin-bottom: -0.5rem; }
@@ -1903,9 +1914,19 @@ a.box:active {
padding: 1.25em 1.5em; }
.content ol {
list-style: decimal outside;
list-style-position: outside;
margin-left: 2em;
margin-top: 1em; }
.content ol:not([type]) {
list-style-type: decimal; }
.content ol:not([type]).is-lower-alpha {
list-style-type: lower-alpha; }
.content ol:not([type]).is-lower-roman {
list-style-type: lower-roman; }
.content ol:not([type]).is-upper-alpha {
list-style-type: upper-alpha; }
.content ol:not([type]).is-upper-roman {
list-style-type: upper-roman; }
.content ul {
list-style: disc outside;
@@ -2239,7 +2260,7 @@ a.box:active {
.select select:not([multiple]) {
padding-right: 2.5em; }
.select select[multiple] {
height: initial;
height: auto;
padding: 0; }
.select select[multiple] option {
padding: 0.5em 1em; }
@@ -2595,12 +2616,13 @@ a.box:active {
border-color: #cfcfcf; }
.file-input {
height: 0.01em;
height: 100%;
left: 0;
opacity: 0;
outline: none;
position: absolute;
top: 0;
width: 0.01em; }
width: 100%; }
.file-cta,
.file-name {
@@ -2696,31 +2718,31 @@ a.box:active {
.field.has-addons .control:last-child .select select {
border-bottom-left-radius: 0;
border-top-left-radius: 0; }
.field.has-addons .control .button:hover, .field.has-addons .control .button.is-hovered,
.field.has-addons .control .input:hover,
.field.has-addons .control .input.is-hovered,
.field.has-addons .control .select select:hover,
.field.has-addons .control .select select.is-hovered {
.field.has-addons .control .button:not([disabled]):hover, .field.has-addons .control .button:not([disabled]).is-hovered,
.field.has-addons .control .input:not([disabled]):hover,
.field.has-addons .control .input:not([disabled]).is-hovered,
.field.has-addons .control .select select:not([disabled]):hover,
.field.has-addons .control .select select:not([disabled]).is-hovered {
z-index: 2; }
.field.has-addons .control .button:focus, .field.has-addons .control .button.is-focused, .field.has-addons .control .button:active, .field.has-addons .control .button.is-active,
.field.has-addons .control .input:focus,
.field.has-addons .control .input.is-focused,
.field.has-addons .control .input:active,
.field.has-addons .control .input.is-active,
.field.has-addons .control .select select:focus,
.field.has-addons .control .select select.is-focused,
.field.has-addons .control .select select:active,
.field.has-addons .control .select select.is-active {
.field.has-addons .control .button:not([disabled]):focus, .field.has-addons .control .button:not([disabled]).is-focused, .field.has-addons .control .button:not([disabled]):active, .field.has-addons .control .button:not([disabled]).is-active,
.field.has-addons .control .input:not([disabled]):focus,
.field.has-addons .control .input:not([disabled]).is-focused,
.field.has-addons .control .input:not([disabled]):active,
.field.has-addons .control .input:not([disabled]).is-active,
.field.has-addons .control .select select:not([disabled]):focus,
.field.has-addons .control .select select:not([disabled]).is-focused,
.field.has-addons .control .select select:not([disabled]):active,
.field.has-addons .control .select select:not([disabled]).is-active {
z-index: 3; }
.field.has-addons .control .button:focus:hover, .field.has-addons .control .button.is-focused:hover, .field.has-addons .control .button:active:hover, .field.has-addons .control .button.is-active:hover,
.field.has-addons .control .input:focus:hover,
.field.has-addons .control .input.is-focused:hover,
.field.has-addons .control .input:active:hover,
.field.has-addons .control .input.is-active:hover,
.field.has-addons .control .select select:focus:hover,
.field.has-addons .control .select select.is-focused:hover,
.field.has-addons .control .select select:active:hover,
.field.has-addons .control .select select.is-active:hover {
.field.has-addons .control .button:not([disabled]):focus:hover, .field.has-addons .control .button:not([disabled]).is-focused:hover, .field.has-addons .control .button:not([disabled]):active:hover, .field.has-addons .control .button:not([disabled]).is-active:hover,
.field.has-addons .control .input:not([disabled]):focus:hover,
.field.has-addons .control .input:not([disabled]).is-focused:hover,
.field.has-addons .control .input:not([disabled]):active:hover,
.field.has-addons .control .input:not([disabled]).is-active:hover,
.field.has-addons .control .select select:not([disabled]):focus:hover,
.field.has-addons .control .select select:not([disabled]).is-focused:hover,
.field.has-addons .control .select select:not([disabled]):active:hover,
.field.has-addons .control .select select:not([disabled]).is-active:hover {
z-index: 4; }
.field.has-addons .control.is-expanded {
flex-grow: 1; }
@@ -2805,6 +2827,7 @@ a.box:active {
margin-right: 0.75rem; } }
.control {
clear: both;
font-size: 1rem;
position: relative;
text-align: left; }
@@ -2963,7 +2986,7 @@ a.box:active {
border-radius: 4px;
padding: 1.25rem 2.5rem 1.25rem 1.5rem;
position: relative; }
.notification a:not(.button) {
.notification a:not(.button):not(.dropdown-item) {
color: currentColor;
text-decoration: underline; }
.notification strong {
@@ -3205,7 +3228,9 @@ a.box:active {
.table.is-hoverable tbody tr:not(.is-selected):hover {
background-color: #242424; }
.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover {
background-color: whitesmoke; }
background-color: #242424; }
.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even) {
background-color: whitesmoke; }
.table.is-narrow td,
.table.is-narrow th {
padding: 0.25em 0.5em; }
@@ -3502,7 +3527,7 @@ a.tag:hover {
position: relative; }
.card-header {
background-color: none;
background-color: transparent;
align-items: stretch;
box-shadow: 0 1px 2px rgba(10, 10, 10, 0.1);
display: flex; }
@@ -3529,11 +3554,11 @@ a.tag:hover {
position: relative; }
.card-content {
background-color: none;
background-color: transparent;
padding: 1.5rem; }
.card-footer {
background-color: none;
background-color: transparent;
border-top: 1px solid #dbdbdb;
align-items: stretch;
display: flex; }
@@ -3591,13 +3616,18 @@ a.tag:hover {
padding: 0.375rem 1rem;
position: relative; }
a.dropdown-item {
a.dropdown-item,
button.dropdown-item {
padding-right: 3rem;
white-space: nowrap; }
a.dropdown-item:hover {
text-align: left;
white-space: nowrap;
width: 100%; }
a.dropdown-item:hover,
button.dropdown-item:hover {
background-color: #0a0a0a;
color: #0a0a0a; }
a.dropdown-item.is-active {
a.dropdown-item.is-active,
button.dropdown-item.is-active {
background-color: #00d1b2;
color: #fff; }
@@ -3623,12 +3653,11 @@ a.dropdown-item {
display: flex; }
.level.is-mobile .level-left + .level-right {
margin-top: 0; }
.level.is-mobile .level-item {
.level.is-mobile .level-item:not(:last-child) {
margin-bottom: 0;
margin-right: 0.75rem; }
.level.is-mobile .level-item:not(:last-child) {
margin-bottom: 0; }
.level.is-mobile .level-item:not(.is-narrow) {
flex-grow: 1; }
.level.is-mobile .level-item:not(.is-narrow) {
flex-grow: 1; }
@media screen and (min-width: 769px), print {
.level {
display: flex; }
@@ -3679,6 +3708,32 @@ a.dropdown-item {
.level-right {
display: flex; } }
.list {
background-color: white;
border-radius: 4px;
box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); }
.list-item {
display: block;
padding: 0.5em 1em; }
.list-item:not(a) {
color: whitesmoke; }
.list-item:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px; }
.list-item:last-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px; }
.list-item:not(:last-child) {
border-bottom: 1px solid #dbdbdb; }
.list-item.is-active {
background-color: #00d1b2;
color: #fff; }
a.list-item {
background-color: #0a0a0a;
cursor: pointer; }
.media {
align-items: flex-start;
display: flex;
@@ -3722,6 +3777,10 @@ a.dropdown-item {
flex-shrink: 1;
text-align: left; }
@media screen and (max-width: 768px) {
.media-content {
overflow-x: auto; } }
.menu {
font-size: 1rem; }
.menu.is-small {
@@ -3891,6 +3950,7 @@ a.dropdown-item {
.modal {
align-items: center;
display: none;
flex-direction: column;
justify-content: center;
overflow: hidden;
position: fixed;
@@ -3927,7 +3987,8 @@ a.dropdown-item {
display: flex;
flex-direction: column;
max-height: calc(100vh - 40px);
overflow: hidden; }
overflow: hidden;
-ms-overflow-y: visible; }
.modal-card-head,
.modal-card-foot {
@@ -3984,6 +4045,8 @@ a.dropdown-item {
color: #0a0a0a; }
.navbar.is-white .navbar-brand .navbar-link::after {
border-color: #0a0a0a; }
.navbar.is-white .navbar-burger {
color: #0a0a0a; }
@media screen and (min-width: 1088px) {
.navbar.is-white .navbar-start > .navbar-item,
.navbar.is-white .navbar-start .navbar-link,
@@ -4022,6 +4085,8 @@ a.dropdown-item {
color: white; }
.navbar.is-black .navbar-brand .navbar-link::after {
border-color: white; }
.navbar.is-black .navbar-burger {
color: white; }
@media screen and (min-width: 1088px) {
.navbar.is-black .navbar-start > .navbar-item,
.navbar.is-black .navbar-start .navbar-link,
@@ -4060,6 +4125,8 @@ a.dropdown-item {
color: #363636; }
.navbar.is-light .navbar-brand .navbar-link::after {
border-color: #363636; }
.navbar.is-light .navbar-burger {
color: #363636; }
@media screen and (min-width: 1088px) {
.navbar.is-light .navbar-start > .navbar-item,
.navbar.is-light .navbar-start .navbar-link,
@@ -4098,6 +4165,8 @@ a.dropdown-item {
color: whitesmoke; }
.navbar.is-dark .navbar-brand .navbar-link::after {
border-color: whitesmoke; }
.navbar.is-dark .navbar-burger {
color: whitesmoke; }
@media screen and (min-width: 1088px) {
.navbar.is-dark .navbar-start > .navbar-item,
.navbar.is-dark .navbar-start .navbar-link,
@@ -4136,6 +4205,8 @@ a.dropdown-item {
color: #fff; }
.navbar.is-primary .navbar-brand .navbar-link::after {
border-color: #fff; }
.navbar.is-primary .navbar-burger {
color: #fff; }
@media screen and (min-width: 1088px) {
.navbar.is-primary .navbar-start > .navbar-item,
.navbar.is-primary .navbar-start .navbar-link,
@@ -4174,6 +4245,8 @@ a.dropdown-item {
color: #fff; }
.navbar.is-link .navbar-brand .navbar-link::after {
border-color: #fff; }
.navbar.is-link .navbar-burger {
color: #fff; }
@media screen and (min-width: 1088px) {
.navbar.is-link .navbar-start > .navbar-item,
.navbar.is-link .navbar-start .navbar-link,
@@ -4212,6 +4285,8 @@ a.dropdown-item {
color: #fff; }
.navbar.is-info .navbar-brand .navbar-link::after {
border-color: #fff; }
.navbar.is-info .navbar-burger {
color: #fff; }
@media screen and (min-width: 1088px) {
.navbar.is-info .navbar-start > .navbar-item,
.navbar.is-info .navbar-start .navbar-link,
@@ -4250,6 +4325,8 @@ a.dropdown-item {
color: #fff; }
.navbar.is-success .navbar-brand .navbar-link::after {
border-color: #fff; }
.navbar.is-success .navbar-burger {
color: #fff; }
@media screen and (min-width: 1088px) {
.navbar.is-success .navbar-start > .navbar-item,
.navbar.is-success .navbar-start .navbar-link,
@@ -4288,6 +4365,8 @@ a.dropdown-item {
color: rgba(0, 0, 0, 0.7); }
.navbar.is-warning .navbar-brand .navbar-link::after {
border-color: rgba(0, 0, 0, 0.7); }
.navbar.is-warning .navbar-burger {
color: rgba(0, 0, 0, 0.7); }
@media screen and (min-width: 1088px) {
.navbar.is-warning .navbar-start > .navbar-item,
.navbar.is-warning .navbar-start .navbar-link,
@@ -4326,6 +4405,8 @@ a.dropdown-item {
color: #fff; }
.navbar.is-danger .navbar-brand .navbar-link::after {
border-color: #fff; }
.navbar.is-danger .navbar-burger {
color: #fff; }
@media screen and (min-width: 1088px) {
.navbar.is-danger .navbar-start > .navbar-item,
.navbar.is-danger .navbar-start .navbar-link,
@@ -4395,6 +4476,7 @@ body.has-navbar-fixed-bottom {
overflow-y: hidden; }
.navbar-burger {
color: #4a4a4a;
cursor: pointer;
display: block;
height: 3.25rem;
@@ -4481,9 +4563,9 @@ a.navbar-item,
flex-grow: 1;
flex-shrink: 1; }
.navbar-link {
.navbar-link:not(.is-arrowless) {
padding-right: 2.5em; }
.navbar-link::after {
.navbar-link:not(.is-arrowless)::after {
border-color: #00d1b2;
margin-top: -0.375em;
right: 1.125em; }
@@ -4645,10 +4727,10 @@ a.navbar-item,
display: block; }
.navbar > .container .navbar-brand,
.container > .navbar .navbar-brand {
margin-left: -1rem; }
margin-left: -.75rem; }
.navbar > .container .navbar-menu,
.container > .navbar .navbar-menu {
margin-right: -1rem; }
margin-right: -.75rem; }
.navbar.is-fixed-bottom-desktop, .navbar.is-fixed-top-desktop {
left: 0;
position: fixed;
@@ -5816,22 +5898,265 @@ label.panel-block {
padding-right: var(--columnGap); }
.columns.is-variable.is-0 {
--columnGap: 0rem; }
@media screen and (max-width: 768px) {
.columns.is-variable.is-0-mobile {
--columnGap: 0rem; } }
@media screen and (min-width: 769px), print {
.columns.is-variable.is-0-tablet {
--columnGap: 0rem; } }
@media screen and (min-width: 769px) and (max-width: 1087px) {
.columns.is-variable.is-0-tablet-only {
--columnGap: 0rem; } }
@media screen and (max-width: 1087px) {
.columns.is-variable.is-0-touch {
--columnGap: 0rem; } }
@media screen and (min-width: 1088px) {
.columns.is-variable.is-0-desktop {
--columnGap: 0rem; } }
@media screen and (min-width: 1088px) and (max-width: 1279px) {
.columns.is-variable.is-0-desktop-only {
--columnGap: 0rem; } }
@media screen and (min-width: 1280px) {
.columns.is-variable.is-0-widescreen {
--columnGap: 0rem; } }
@media screen and (min-width: 1280px) and (max-width: 1471px) {
.columns.is-variable.is-0-widescreen-only {
--columnGap: 0rem; } }
@media screen and (min-width: 1472px) {
.columns.is-variable.is-0-fullhd {
--columnGap: 0rem; } }
.columns.is-variable.is-1 {
--columnGap: 0.25rem; }
@media screen and (max-width: 768px) {
.columns.is-variable.is-1-mobile {
--columnGap: 0.25rem; } }
@media screen and (min-width: 769px), print {
.columns.is-variable.is-1-tablet {
--columnGap: 0.25rem; } }
@media screen and (min-width: 769px) and (max-width: 1087px) {
.columns.is-variable.is-1-tablet-only {
--columnGap: 0.25rem; } }
@media screen and (max-width: 1087px) {
.columns.is-variable.is-1-touch {
--columnGap: 0.25rem; } }
@media screen and (min-width: 1088px) {
.columns.is-variable.is-1-desktop {
--columnGap: 0.25rem; } }
@media screen and (min-width: 1088px) and (max-width: 1279px) {
.columns.is-variable.is-1-desktop-only {
--columnGap: 0.25rem; } }
@media screen and (min-width: 1280px) {
.columns.is-variable.is-1-widescreen {
--columnGap: 0.25rem; } }
@media screen and (min-width: 1280px) and (max-width: 1471px) {
.columns.is-variable.is-1-widescreen-only {
--columnGap: 0.25rem; } }
@media screen and (min-width: 1472px) {
.columns.is-variable.is-1-fullhd {
--columnGap: 0.25rem; } }
.columns.is-variable.is-2 {
--columnGap: 0.5rem; }
@media screen and (max-width: 768px) {
.columns.is-variable.is-2-mobile {
--columnGap: 0.5rem; } }
@media screen and (min-width: 769px), print {
.columns.is-variable.is-2-tablet {
--columnGap: 0.5rem; } }
@media screen and (min-width: 769px) and (max-width: 1087px) {
.columns.is-variable.is-2-tablet-only {
--columnGap: 0.5rem; } }
@media screen and (max-width: 1087px) {
.columns.is-variable.is-2-touch {
--columnGap: 0.5rem; } }
@media screen and (min-width: 1088px) {
.columns.is-variable.is-2-desktop {
--columnGap: 0.5rem; } }
@media screen and (min-width: 1088px) and (max-width: 1279px) {
.columns.is-variable.is-2-desktop-only {
--columnGap: 0.5rem; } }
@media screen and (min-width: 1280px) {
.columns.is-variable.is-2-widescreen {
--columnGap: 0.5rem; } }
@media screen and (min-width: 1280px) and (max-width: 1471px) {
.columns.is-variable.is-2-widescreen-only {
--columnGap: 0.5rem; } }
@media screen and (min-width: 1472px) {
.columns.is-variable.is-2-fullhd {
--columnGap: 0.5rem; } }
.columns.is-variable.is-3 {
--columnGap: 0.75rem; }
@media screen and (max-width: 768px) {
.columns.is-variable.is-3-mobile {
--columnGap: 0.75rem; } }
@media screen and (min-width: 769px), print {
.columns.is-variable.is-3-tablet {
--columnGap: 0.75rem; } }
@media screen and (min-width: 769px) and (max-width: 1087px) {
.columns.is-variable.is-3-tablet-only {
--columnGap: 0.75rem; } }
@media screen and (max-width: 1087px) {
.columns.is-variable.is-3-touch {
--columnGap: 0.75rem; } }
@media screen and (min-width: 1088px) {
.columns.is-variable.is-3-desktop {
--columnGap: 0.75rem; } }
@media screen and (min-width: 1088px) and (max-width: 1279px) {
.columns.is-variable.is-3-desktop-only {
--columnGap: 0.75rem; } }
@media screen and (min-width: 1280px) {
.columns.is-variable.is-3-widescreen {
--columnGap: 0.75rem; } }
@media screen and (min-width: 1280px) and (max-width: 1471px) {
.columns.is-variable.is-3-widescreen-only {
--columnGap: 0.75rem; } }
@media screen and (min-width: 1472px) {
.columns.is-variable.is-3-fullhd {
--columnGap: 0.75rem; } }
.columns.is-variable.is-4 {
--columnGap: 1rem; }
@media screen and (max-width: 768px) {
.columns.is-variable.is-4-mobile {
--columnGap: 1rem; } }
@media screen and (min-width: 769px), print {
.columns.is-variable.is-4-tablet {
--columnGap: 1rem; } }
@media screen and (min-width: 769px) and (max-width: 1087px) {
.columns.is-variable.is-4-tablet-only {
--columnGap: 1rem; } }
@media screen and (max-width: 1087px) {
.columns.is-variable.is-4-touch {
--columnGap: 1rem; } }
@media screen and (min-width: 1088px) {
.columns.is-variable.is-4-desktop {
--columnGap: 1rem; } }
@media screen and (min-width: 1088px) and (max-width: 1279px) {
.columns.is-variable.is-4-desktop-only {
--columnGap: 1rem; } }
@media screen and (min-width: 1280px) {
.columns.is-variable.is-4-widescreen {
--columnGap: 1rem; } }
@media screen and (min-width: 1280px) and (max-width: 1471px) {
.columns.is-variable.is-4-widescreen-only {
--columnGap: 1rem; } }
@media screen and (min-width: 1472px) {
.columns.is-variable.is-4-fullhd {
--columnGap: 1rem; } }
.columns.is-variable.is-5 {
--columnGap: 1.25rem; }
@media screen and (max-width: 768px) {
.columns.is-variable.is-5-mobile {
--columnGap: 1.25rem; } }
@media screen and (min-width: 769px), print {
.columns.is-variable.is-5-tablet {
--columnGap: 1.25rem; } }
@media screen and (min-width: 769px) and (max-width: 1087px) {
.columns.is-variable.is-5-tablet-only {
--columnGap: 1.25rem; } }
@media screen and (max-width: 1087px) {
.columns.is-variable.is-5-touch {
--columnGap: 1.25rem; } }
@media screen and (min-width: 1088px) {
.columns.is-variable.is-5-desktop {
--columnGap: 1.25rem; } }
@media screen and (min-width: 1088px) and (max-width: 1279px) {
.columns.is-variable.is-5-desktop-only {
--columnGap: 1.25rem; } }
@media screen and (min-width: 1280px) {
.columns.is-variable.is-5-widescreen {
--columnGap: 1.25rem; } }
@media screen and (min-width: 1280px) and (max-width: 1471px) {
.columns.is-variable.is-5-widescreen-only {
--columnGap: 1.25rem; } }
@media screen and (min-width: 1472px) {
.columns.is-variable.is-5-fullhd {
--columnGap: 1.25rem; } }
.columns.is-variable.is-6 {
--columnGap: 1.5rem; }
@media screen and (max-width: 768px) {
.columns.is-variable.is-6-mobile {
--columnGap: 1.5rem; } }
@media screen and (min-width: 769px), print {
.columns.is-variable.is-6-tablet {
--columnGap: 1.5rem; } }
@media screen and (min-width: 769px) and (max-width: 1087px) {
.columns.is-variable.is-6-tablet-only {
--columnGap: 1.5rem; } }
@media screen and (max-width: 1087px) {
.columns.is-variable.is-6-touch {
--columnGap: 1.5rem; } }
@media screen and (min-width: 1088px) {
.columns.is-variable.is-6-desktop {
--columnGap: 1.5rem; } }
@media screen and (min-width: 1088px) and (max-width: 1279px) {
.columns.is-variable.is-6-desktop-only {
--columnGap: 1.5rem; } }
@media screen and (min-width: 1280px) {
.columns.is-variable.is-6-widescreen {
--columnGap: 1.5rem; } }
@media screen and (min-width: 1280px) and (max-width: 1471px) {
.columns.is-variable.is-6-widescreen-only {
--columnGap: 1.5rem; } }
@media screen and (min-width: 1472px) {
.columns.is-variable.is-6-fullhd {
--columnGap: 1.5rem; } }
.columns.is-variable.is-7 {
--columnGap: 1.75rem; }
@media screen and (max-width: 768px) {
.columns.is-variable.is-7-mobile {
--columnGap: 1.75rem; } }
@media screen and (min-width: 769px), print {
.columns.is-variable.is-7-tablet {
--columnGap: 1.75rem; } }
@media screen and (min-width: 769px) and (max-width: 1087px) {
.columns.is-variable.is-7-tablet-only {
--columnGap: 1.75rem; } }
@media screen and (max-width: 1087px) {
.columns.is-variable.is-7-touch {
--columnGap: 1.75rem; } }
@media screen and (min-width: 1088px) {
.columns.is-variable.is-7-desktop {
--columnGap: 1.75rem; } }
@media screen and (min-width: 1088px) and (max-width: 1279px) {
.columns.is-variable.is-7-desktop-only {
--columnGap: 1.75rem; } }
@media screen and (min-width: 1280px) {
.columns.is-variable.is-7-widescreen {
--columnGap: 1.75rem; } }
@media screen and (min-width: 1280px) and (max-width: 1471px) {
.columns.is-variable.is-7-widescreen-only {
--columnGap: 1.75rem; } }
@media screen and (min-width: 1472px) {
.columns.is-variable.is-7-fullhd {
--columnGap: 1.75rem; } }
.columns.is-variable.is-8 {
--columnGap: 2rem; }
@media screen and (max-width: 768px) {
.columns.is-variable.is-8-mobile {
--columnGap: 2rem; } }
@media screen and (min-width: 769px), print {
.columns.is-variable.is-8-tablet {
--columnGap: 2rem; } }
@media screen and (min-width: 769px) and (max-width: 1087px) {
.columns.is-variable.is-8-tablet-only {
--columnGap: 2rem; } }
@media screen and (max-width: 1087px) {
.columns.is-variable.is-8-touch {
--columnGap: 2rem; } }
@media screen and (min-width: 1088px) {
.columns.is-variable.is-8-desktop {
--columnGap: 2rem; } }
@media screen and (min-width: 1088px) and (max-width: 1279px) {
.columns.is-variable.is-8-desktop-only {
--columnGap: 2rem; } }
@media screen and (min-width: 1280px) {
.columns.is-variable.is-8-widescreen {
--columnGap: 2rem; } }
@media screen and (min-width: 1280px) and (max-width: 1471px) {
.columns.is-variable.is-8-widescreen-only {
--columnGap: 2rem; } }
@media screen and (min-width: 1472px) {
.columns.is-variable.is-8-fullhd {
--columnGap: 2rem; } }
.tile {
align-items: stretch;
@@ -6356,16 +6681,18 @@ label.panel-block {
.hero.is-large .hero-body {
padding-bottom: 18rem;
padding-top: 18rem; } }
.hero.is-halfheight .hero-body, .hero.is-fullheight .hero-body {
.hero.is-halfheight .hero-body, .hero.is-fullheight .hero-body, .hero.is-fullheight-with-navbar .hero-body {
align-items: center;
display: flex; }
.hero.is-halfheight .hero-body > .container, .hero.is-fullheight .hero-body > .container {
.hero.is-halfheight .hero-body > .container, .hero.is-fullheight .hero-body > .container, .hero.is-fullheight-with-navbar .hero-body > .container {
flex-grow: 1;
flex-shrink: 1; }
.hero.is-halfheight {
min-height: 50vh; }
.hero.is-fullheight {
min-height: 100vh; }
.hero.is-fullheight-with-navbar {
min-height: calc(100vh - 3.25rem); }
.hero-video {
overflow: hidden; }
+4
View File
@@ -1,3 +1,7 @@
; This is the service that broadcast market data to the
; world. The users need to expect websocket and REST api.
(ns skirmish.bookkeeper.core
(:require [org.httpkit.server :refer [run-server]]
[compojure.handler :refer [api]]
+44 -7
View File
@@ -4,15 +4,50 @@
[datomic.api :as d]
[slingshot.slingshot :refer [try+ throw+]]
[skirmish.rest.util :refer [build-response not-found-resp get-body]]
[taoensso.timbre :as timbre]))
[taoensso.timbre :as timbre]
[skirmish.routes.core :as routes]
[clj-http.client :as http-client]))
(defn create-user [conn]
(defn generate-amount-for-new-user [seed]
(let [random (new java.util.Random seed)
random-fn #(. random nextInt 20000)]
(* 100 (reduce + (take 100 (repeatedly random-fn))))))
(defn create-account [account-id sec-id]
#:skirmish.broker.account{:id account-id
:sec-id sec-id
:private-key (str account-id)})
(defn create-user-account [seed default-sec-id]
(let [url (routes/get-path-for :ledger/security {:sec-id (name default-sec-id)})
account-id (java.util.UUID/randomUUID)
amount (generate-amount-for-new-user seed)
payload [amount (str account-id)]]
(timbre/info "Creating new account " account-id " with ammount " amount " of " default-sec-id)
(timbre/info url)
(http-client/post url {:body (str payload)})
(create-account account-id default-sec-id)))
(defn create-user-seed-from-email [email]
(let [hasher (java.security.MessageDigest/getInstance "SHA-256")
salt (. "this should be private and set in config" getBytes "utf-8")
msg (. email getBytes "utf-8")
_ (doto hasher (.update salt) (.update msg))
hash (. hasher digest)]
(int (mod (new BigInteger 1 hash) (biginteger Integer/MAX_VALUE)))))
(defn create-user [conn default-sec-id]
(fn [{body :body :as request}]
(try+
(let [email (-> body get-body :email)]
(timbre/info "Creating new user" email)
@(d/transact conn [[:skirmish.broker.user/create-user email]])
(let [email (-> body get-body :email)
_ (timbre/info "Creating new user" email)
seed (create-user-seed-from-email email)
account (create-user-account seed default-sec-id)]
@(d/transact conn [[:skirmish.broker.user/create-user email [account]]])
(build-response request {:status 201 :headers {"location" (str "/api/1/users/" email)}}))
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
@@ -24,6 +59,7 @@
ret (map #(assoc % :skirmish.broker.account/url (url %)) accounts)]
(assoc user :skirmish.broker.user/accounts ret)))
(defn get-user [conn]
(fn [{{email :email} :route-params :as request}]
(try+
@@ -54,14 +90,15 @@
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
(defn get-account [conn]
(fn [email account]))
(defn create-routes [conn]
(defn create-routes [conn config]
(routes
(context "/api/1" []
(POST "/users" [] (create-user conn))
(POST "/users" [] (create-user conn (:default-sec-id config)))
(GET "/users" [] (get-all-users conn))
(GET "/users/:email" [email] (get-user conn))
(GET "/users/:email/accounts/:account" [email account] (get-account conn)))
+3 -1
View File
@@ -1,3 +1,5 @@
; The backend of the skirmish web user interface.
(ns skirmish.broker.core
(:require [org.httpkit.server :refer [run-server]]
[compojure.handler :refer [api]]
@@ -11,7 +13,7 @@
(defn -main [config securities-api exchange-apis]
(let [webserver-config (:api-broker config)
conn (create-connection (-> webserver-config :datomic :transactor-url))
api-routes (create-routes conn)]
api-routes (create-routes conn webserver-config)]
(timbre/info "Running skirmish.broker.core server with config: " webserver-config)
(run-server
(-> api-routes api cors-middleware)
+2 -3
View File
@@ -8,12 +8,11 @@
:db/doc "Create a new user in the system."
:db/fn (d/function {:lang "clojure"
:requires ['[slingshot.slingshot]]
:params '[db email]
:params '[db email accounts]
:code '(let [existing-user (d/pull db '[:db/id] [:skirmish.broker.user/email email])]
;:skirmish.broker.user/accounts []])]
(when (-> existing-user :db/id some?)
(slingshot.slingshot/throw+ {:error-type :4XX :status 400 :body {:error "A user with this email already exists."}}))
[#:skirmish.broker.user{:email email}])})}])
[#:skirmish.broker.user{:email email :accounts accounts}])})}])
(def accounts-schema [
-104
View File
@@ -1,104 +0,0 @@
(ns skirmish.cli.benchmark
(:require [clojure.math.combinatorics :refer [cartesian-product]]
[clojure.core.async :refer [go chan <! >! close! to-chan close!] :as async]
[gniazdo.core :as ws]
[clj-http.client :as client]
[com.stuartsierra.frequencies :as freq]
[clojure.edn :as edn]))
(def ids (atom 0))
(defn web-socket-to-channel [url output-ch]
(let [on-receive #(go (>! output-ch %))
headers {"accept" "application/edn"}]
(ws/connect url :headers headers :on-receive on-receive)))
(defn create-orders-channel [securities]
(let [output (chan 1 (map edn/read-string))
url #(format "ws://localhost:%s/api/1/securities/%s/orders" 8080 %)
xf (comp (map :sec-id)
(map name)
(map url)
(map #(web-socket-to-channel % output)))]
(doall (sequence xf securities))
(async/pub output :book.order/id)))
(defn create-requests-payload-for-security [i security id]
(let [rd (new java.util.Random i)
rand-bool (= (mod (. rd nextInt) 2) 0) ;; java random number generator REALLY sucks
type (if rand-bool :sell :buy)
price (+ (* 3 (. rd nextGaussian)) 30)
int-price (int (* price 100))
quantity (reduce + 1 (map #(. rd nextInt (inc %)) (range 100)))]
{:type type :quantity quantity :price int-price :security security :benchmark/id id}))
(defn create-requests-for-security [[i security]]
(let [req-id (swap! ids inc)
payload (create-requests-payload-for-security i (str (:sec-id security)) req-id)
sec-id-str (-> security :sec-id name)
params {:throw-exceptions false :content-type :edn :headers {"accept" "application/edn"}}
url (format "http://localhost:%s/api/1/securities/%s" 8080 sec-id-str)]
(fn []
(let [now (System/nanoTime)]
(client/post url (assoc params :form-params (assoc payload :benchmark/request-time now)))))))
(defn make-requests [n securities]
(map create-requests-for-security (cartesian-product (range n) securities)))
(defn no-more-then-n-simultaneously [f input-chan n]
(let [buffer (chan)]
(go (async/pipeline n buffer (map f) input-chan))
buffer))
(defn decode-response [response]
(let [now (System/nanoTime)
ret {}
ret (assoc ret :status (:status response))
ret (assoc ret :order (edn/read-string (:body response)))
ret (assoc ret :duration (/ (- now (get-in ret [:order :benchmark/request-time])) (Math/pow 10 9)))]
ret))
(defn benchmark [requests orders]
(let [decoded-responses (chan 1 (map decode-response))
responses (no-more-then-n-simultaneously #(%) (to-chan requests) 10)]
(go (async/pipe responses decoded-responses))
decoded-responses))
(defn compute-result [results new-result]
(let [status-code (:status new-result)
duration (:duration new-result)
old-status-code-count (get-in results [:status status-code] 0)
ret results
ret (assoc-in ret [:status status-code] (inc old-status-code-count))
ret (assoc ret :durations (conj (:durations ret) duration))]
ret))
(defn pp-durations [durations]
(let [freq-map (frequencies durations)]
(print (freq/stats freq-map))))
(defn display-results [results]
(println results)
(println "number of request: " (:n results))
(println "status map: " (:status results))
(pp-durations (:durations results)))
(defn -main [config exchange-apis]
(let [benchmark-config (:benchmark config)
requests (-> benchmark-config :number-of-requests-per-securities (make-requests (:securities config)))
orders (create-orders-channel (:securities config))
results (benchmark requests orders)]
(display-results (async/<!! (async/reduce compute-result {} results)))))
-1
View File
@@ -1 +0,0 @@
(ns skirmish.cli.core)
-56
View File
@@ -1,56 +0,0 @@
(ns skirmish.cli.exchange
(:require [skirmish.exchange.match :refer [sell?]]
[skirmish.util :refer :all]
[skirmish.exchange.exchange :refer [empty-book create-exchange-for-security]]
[skirmish.order] ;; for spec side effect
[slingshot.slingshot :refer [try+ throw+]]
[clojure.edn :as edn]
[clojure.core.async :refer [chan go >!! <!!]]
[clojure.spec.alpha :as s]))
(def incoming-order-channel (chan))
;(def trades-channel (chan))
;
;(def new-book-order-channel (chan))
(defn print-chan [prefix c]
(loop [shit (<!! c)]
(println prefix shit)
(recur (<!! c))))
;(def master-book {:sell (create-sell-book)
; :buy (create-buy-book)})
;;; repl things.
(defn read-order []
(try
(print "$ ")
(let [order (edn/read-string (read-line))
valid? (s/valid? :book.order/order order)]
(if valid? order nil))
(catch Exception e nil)))
(def order-ids (atom -1))
(defn add-id-to-order [order]
(assoc order :book.order/id (swap! order-ids inc)))
(defn -main [& args]
(let [initial-book empty-book
incoming-order-channel (chan)
[trades-channel new-book-order-channel] (create-exchange-for-security initial-book incoming-order-channel)]
(go (print-chan "trade > " trades-channel))
(go (print-chan "order > " new-book-order-channel))
(loop [order (read-order)]
(if (some? order)
(do
(let [order-with-id (add-id-to-order order)]
(>!! incoming-order-channel order-with-id)
(println "\n\norder accepted with id: " (:book.order/id order-with-id))))
(println "invalid order"))
(recur (read-order)))))
+1 -3
View File
@@ -1,7 +1,5 @@
(ns skirmish.core
(:require [skirmish.cli.core]
[skirmish.cli.benchmark]
[skirmish.exchange.core]
(:require [skirmish.exchange.core]
[skirmish.wui.server]
[skirmish.bookkeeper.core]
[skirmish.exchange-gateway.core]
-3
View File
@@ -106,6 +106,3 @@
(<! orders-copy)
(<! trades-copy)
(recur (<! new-order-publisher) new-book)))))
+5 -1
View File
@@ -153,6 +153,11 @@
(defn emit-securities [conn]
"Emit securities in the specified wallet. This is the only way to
create securities.
payload
[:amount :acount]"
(fn [{{sec-id-str :sec-id} :route-params body :body :as request}]
(try+
(let [emit-request (-> body get-body (get-emit-securities-request (keyword sec-id-str)))]
@@ -184,7 +189,6 @@
(defn create-routes-for-ledger [conn]
(compojure/routes
;; (routes/get-context :ledger)
(routes/context :ledger []
(routes/GET :ledger/home [] get-api-description)
(routes/GET :ledger/accounts [] (get-available-accounts conn))
+2 -1
View File
@@ -26,7 +26,8 @@
(head)
[:body.body-container
mount-target
(include-js "/js/app.js")]))
(include-js "/js/app.js")
(include-js "https://sidecar.gitter.im/dist/sidecar.v1.js")]))
(defn loading-page []
(html5
@@ -1,14 +1,10 @@
(ns skirmish.wui.components.home-page
(:require [cljs-http.client :as http]
[reagent.core :refer [atom]]
[skirmish.wui.util :refer [email?]]
[cljs.core.async :refer [<!] :refer-macros [go]]
[secretary.core :as secretary]))
(defn email? [s]
(let [email-pattern #"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"]
(->> s str (re-matches email-pattern) some?)))
(defn create-user [email]
(go
(let [url "http://localhost:1605/api/1/users"
@@ -0,0 +1,69 @@
(ns skirmish.wui.components.otc-page
(:require [reagent.core :refer [atom]]
[skirmish.wui.util :refer [email?]]))
(defn set-amount [state event]
(let [inserted-value (-> event .-target .-value)
new-value (first (re-find #"^\d+(\.\d*)?" inserted-value))]
(swap! state #(assoc % :value new-value))))
(defn set-email [state event]
(let [new-email (-> event .-target .-value)]
(swap! state #(assoc % :email new-email))))
(defn set-sec-id [state event]
(let [sec-id (-> event .-target .-value)]
(swap! state #(assoc % :sec-id sec-id))))
(defn ready-to-transfer? [state]
(and (-> state :value js/parseInt pos?)
(-> state :email email?)))
(defn direct-send-confirmation []
[:div.model
[:div.modal-background]
[:div.modal-content
[:div.modal-card
[:header.modal-card-head
[:p.modal-card-title "Modal title"]
[:button.delete]]]]
[:a.button.modal-close.is-large {:aria-label "close"} "dslkds"]])
(defn direct-send []
(let [state (atom {})]
(fn []
[:div
[:div.content
[:p "Send securities to another person."]
[:pre (str @state)]]
[:form.form
[:div.field.has-addons.has-addons-centered
[:div.control
[:span.select
[:select {:value (:sec-id @state)
:on-change (partial set-sec-id state)}
[:option {:value :foo :selected true} "damp"]
[:option {:value :bar } "blah blah blah"]]]]
[:div.control
[:input.input {:type "text"
:value (:value @state)
:on-change (partial set-amount state)
:placeholder "110.90"}]]
[:div.control
[:input.input {:type "text"
:value (:email @state)
:on-change (partial set-email state)
:placeholder "boris.johnson@redneck.co.uk"}]]
[:a.button.is-primary {:style {:marginLeft "1em"}
:disabled (-> @state ready-to-transfer? not)} "transfer"]]]])))
(defn page [state]
[:div
;[direct-send-confirmation]
[:div.content
[:h5 "Do transaction without passing by the exchange."]]
[direct-send]])
@@ -23,8 +23,9 @@
accounts (map :body (<! (async/into [] ar)))]
(reset! a-accounts accounts))))
(defn shit [urls accounts]
(defn page-wrapper [accounts]
[:div.content
[:h5 "View all the assets you own."]
[make-portfolio-summary-table accounts]])
@@ -32,4 +33,4 @@
(let [urls (map :skirmish.broker.account/url accounts)
a-accounts (atom [])]
(get-accounts-information urls a-accounts)
#(shit urls @a-accounts)))
#(page-wrapper @a-accounts)))
@@ -49,7 +49,9 @@
(defn create-currencies-table [securities]
[:div
[:div.content
[:h3 "Currencies"]]
[:h3 "Currencies"]
[:p "A " [:em "currency"] " is currency that pays interest rate in
its own nomination."]]
[:table.table.is-hoverable.is-fullwidth
table-head
[:tbody
@@ -58,7 +60,11 @@
(defn create-equities-table [securities]
[:div
[:div.content
[:h3 "Equities"]]
[:h3 "Equities"]
[:p "An " [:em "equity"] " is a type of security that pays dividends. In
real life, an equity (or stock) is a share in a company ownership.
When the company make profits, it shares the its profits between its stock
holder in a pro rata fashion."]]
[:table.table.is-hoverable.is-fullwidth
table-head
[:tbody
@@ -76,4 +82,7 @@
#(create-securities-table @securities)))
(defn page [state]
(security-table))
[:div
[:div.content
[:h5 "View the available securities for trading."]]
[security-table]])
@@ -6,12 +6,29 @@
(def done {:style {:text-decoration "line-through"}})
(defn filter-out-done [n]
(let [state (volatile! n)]
(fn [item]
(when (= (second item) done)
(vswap! state dec))
(or (not= (second item) done) (neg? @state)))))
(defn todo-list [& items]
(let [filter-fn (filter-out-done (- (count items) 20))
item-to-show (filter filter-fn items)]
[:ol
(for [item item-to-show]
item)]))
(defn page []
[:div.content
[:h5 "This still to do in this project. This is a jira for the poor (or for the geek)."]
[:ol
[:li "Broker"
[:p "the brooker should use the exchange-gateway and ledge as apis. Nothing else."]
[:ol
[todo-list
[:li done "implement basic login in the wui"]
[:li done "implement basic web server"]
[:li done "implement stupid login on server side."]
@@ -22,14 +39,18 @@
[:li done "make the " [:a {:href "/portfolio"} "portfolio"] " page representing the use accounts"]
[:li done "make a somewhat pretty " [:a {:href "/securities/dapm"} "security"] " page"]
[:li done "create two stocks"]
[:li on-it "add stocks to didier@gmail.com"]
[:li on-it "make something for urls on the backend"]
[:li done "add stocks to didier@gmail.com"]
[:li done "make something for urls on the backend"]
[:li on-it "add a wui for OTC"]
[:li "fix the stupid chat. This is an important feature for OTC users."]
[:li "make something that allows to send securities from one user to another"]
[:li "move 'routes' in 'rest'"]
[:li "add a little bit of professionalism on top of the broker api"]
[:li "make the api support transit and edn"]
[:li "implements interest rate in ledger."]
[:li "Make sure the ledger can have accounts"]
[:li "when a new user signs in, he should receive some sort of money"]
[:li "\u22EE"]
[:li done "when a new user signs in, he should receive some sort of money"]
[:li "implement send securities to friends"]
[:li "implement buy/sell securities."]
[:li "Use ledger as source of through in each sub server."]
[:li "Update web ui for integrating securities with ledger."]
+3 -2
View File
@@ -37,8 +37,9 @@
[:div.navbar-start
[:a.navbar-item.is-tab {:href "/" :class (top-bar-class state :home)} "Home"]
[:a.navbar-item.is-tab {:href "/securities" :class (top-bar-class state :securities)} "Securities"]
[:a.navbar-item.is-tab {:href "/create-order" :class (top-bar-class state :create-order)} "Create order"]
[:a.navbar-item.is-tab {:href "/portfolio" :class (top-bar-class state :view-portfolio)} "View Portfolio"]
[:a.navbar-item.is-tab {:href "/create-order" :class (top-bar-class state :create-order)} "Create Order"]
[:a.navbar-item.is-tab {:href "/otc" :class (top-bar-class state :otc)} "Over The Counter"]
[:a.navbar-item.is-tab {:href "/portfolio" :class (top-bar-class state :view-portfolio)} "Portfolio"]
[:a.navbar-item.is-tab {:href "/todo" :class (top-bar-class state :todo)} "Todo"]]
[:div.navbar-end
[:a.navbar-item {} "Skirmish Shitty Exchange"]
+7
View File
@@ -53,6 +53,12 @@
(session/put! :current-page #'pages/portfolio-page)
(swap! state #(into % {:page :portfolio})))
(secretary/defroute "/otc" []
(session/put! :current-page #'pages/otc-page)
(swap! state #(into % {:page :otc})))
(secretary/defroute "/create-order" []
(session/put! :current-page #'pages/create-order-page)
(swap! state #(into % {:page :create-order})))
@@ -77,6 +83,7 @@
[(session/get :current-page) @state]]]]])
(defn mount-root []
(reagent/render [current-page] (.getElementById js/document "app")))
+4 -1
View File
@@ -4,7 +4,8 @@
[skirmish.wui.components.portfolio-page]
[skirmish.wui.components.create-order-page]
[skirmish.wui.components.securities-page]
[skirmish.wui.components.security-page]))
[skirmish.wui.components.security-page]
[skirmish.wui.components.otc-page]))
(def home-page skirmish.wui.components.home-page/page)
@@ -17,3 +18,5 @@
(def portfolio-page skirmish.wui.components.portfolio-page/page)
(def security-page skirmish.wui.components.security-page/page)
(def otc-page skirmish.wui.components.otc-page/page)
+5
View File
@@ -27,3 +27,8 @@
(pp-int n)
(let [i (int (Math/pow 10 dec))]
(str (pp-int (quot n i)) "," (right-pad (str (mod n i)) "0" dec))))))
(defn email? [s]
(let [email-pattern #"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"]
(->> s str (re-matches email-pattern) some?)))
+4
View File
@@ -25,6 +25,10 @@ $table-background-color: $grey-dark;
$table-cell-heading-color: $grey-darker;
$table-row-hover-background-color: $black-ter;
// box
$box-background-color: $grey;
$box-color: $white;
$box-shadow: 0 2px 3px $grey-light, 0 0 0 1px $grey-light;// $red;
@import "../../node_modules/bulma/sass/base/_all";
+19 -1
View File
@@ -1,7 +1,9 @@
(ns skirmish.broker.broker-test
(:require [midje.sweet :refer :all]
[orchestra.spec.test :as st]
[skirmish.broker.broker :refer [add-account-urls]]))
[skirmish.broker.broker :refer [add-account-urls
create-user-seed-from-email
generate-amount-for-new-user]]))
(st/instrument)
@@ -13,3 +15,19 @@
:skirmish.broker.account/url "http://localhost:1604/api/1/ledger/accounts/foo"}]}]
(add-account-urls user) => ret)))
(facts "about the new users"
(fact "we should add some random amount of damp in their account"
(let [random-values (map generate-amount-for-new-user (range 10000))
sum (reduce + random-values)
mean (/ sum (count random-values))
min-data (apply min random-values)
max-data (apply max random-values)]
(int mean) => 100027427
min-data => 78422400
max-data => 120128000))
(fact "we should generate the random seed by the email"
(create-user-seed-from-email "foo@gmail.com") => 399122929
(create-user-seed-from-email "boo@gmail.com") => 1433675645))
+8 -4
View File
@@ -2,7 +2,8 @@
(:require [midje.sweet :refer :all]
[orchestra.spec.test :as st]
[datomic.api :as d]
[skirmish.broker.db :refer [create-connection]]))
[skirmish.broker.db :refer [create-connection]]
[skirmish.broker.broker :refer [create-account]]))
(st/instrument)
@@ -15,7 +16,10 @@
(fact "should be able to create new users"
(let [conn (create-conn)
email "foo@gmail.com"
_ @(d/transact conn [[:skirmish.broker.user/create-user email]])
account-id #uuid"dc0d1123-e659-4e8a-b38b-ece520f4c4d0"
account (create-account account-id :dapm)
_ @(d/transact conn [[:skirmish.broker.user/create-user email account]])
db (d/db conn)
user (d/pull db '[*] [:skirmish.broker.user/email email])]
(:skirmish.broker.user/email user) => email)));
user (d/pull db '[* {:skirmish.broker.user/accounts [*]}] [:skirmish.broker.user/email email])]
(:skirmish.broker.user/email user) => email
(-> user :skirmish.broker.user/accounts first :skirmish.broker.account/id) => account-id)))
-30
View File
@@ -1,30 +0,0 @@
(ns skirmish.cli.benchmark-test
(:require [skirmish.cli.benchmark :refer :all]
[midje.sweet :refer :all]
[clojure.core.async :as async]))
(facts "creating simple orders"
(fact "buy order"
(let [buy-order (create-requests-payload-for-security 666 (str :equity/ibm) 1)
expt {:benchmark/id 1 :price 2922 :quantity 2476 :security ":equity/ibm" :type :buy}]
buy-order => expt));;
(fact "sell order"
(let [buy-order (create-requests-payload-for-security 667 (str :equity/ibm) 2)
expt {:benchmark/id 2 :price 2740 :quantity 2516 :security ":equity/ibm" :type :sell}]
buy-order => expt)))
(facts "the utility functions"
(fact "no-more-then-n-simultaneously"
(let [simultatious (atom [])
input-chan (async/to-chan (range 10))
sleep-time #(+ (mod (+ (* 5142 %) 8765) 200) 50)
f (fn [i]
(swap! simultatious #(conj % {:id i :status :start :value 1}))
(Thread/sleep (sleep-time i))
(swap! simultatious #(conj % {:id i :status :end :value -1}))
i)
res (async/<!! (async/into [] (no-more-then-n-simultaneously f input-chan 3)))
simultatious-over-time (reductions #(+ %1 (:value %2)) 0 @simultatious)]
(apply max simultatious-over-time) => 3
(first simultatious-over-time) => 0
(last simultatious-over-time) => 0)))