![Create React App Officially Deprecated Amid React 19 Compatibility Issues](https://cdn.sanity.io/images/cgdhsj6q/production/04fa08cf844d798abc0e1a6391c129363cc7e2ab-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Create React App Officially Deprecated Amid React 19 Compatibility Issues
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.
currency-market
Advanced tools
A synchronous implementation of a limit order based currency market
A synchronous implementation of a limit order based currency market
npm install currency-market
The currency-market
package is intended to be used by a system of components that need to be synchronized. Those components are
On intialization
var Engine = require('currency-market').Engine;
var Amount = require('currency-market').Amount;
var COMMISSION_RATE = new Amount('0.001');
var COMMISSION_REFERENCE = '0.1%';
var engine = new Engine({
commission: {
account: 'commission'
calculate: function(params) {
return {
amount: params.amount.multiply(COMMISSION_RATE),
reference: COMMISSION_REFERENCE
};
}
},
json: previousEngineState
});
...
sendStateToDeltaHub(JSON.stringify(engine));
var State = require('currency-market').State;
getStateFromEngine(function(receivedEngineState){
var state = new State({
commission: {
account: 'commission'
},
json: receivedEngineState
});
});
...
sendStateToFrontEnd(JSON.stringify(state));
var State = require('currency-market').State;
getStateFromDeltaHub(function(receivedState){
var state = new State({
commission: {
account: 'commission'
},
json: receivedState
});
});
Then the life cycle of an operation is as follows
var Operation = require('currency-market').Operation;
var Amount = require('currency-market').Amount;
var operation = new Operation({
reference: '550e8400-e29b-41d4-a716-446655440000',
account: 'Peter',
deposit: {
currency: 'EUR',
amount: new Amount '500'
}
});
sendToOperationHub(JSON.stringify(operation));
var Operation = require('currency-market').Operation;
var operation = new Operation({
json: receivedJSON
});
operation.accept({
sequence: 654852,
timestamp: 1371737390976
});
sendToEngines(JSON.stringify(operation));
var Operation = require('currency-market').Operation;
var operation = new Operation({
json: receivedJSON
});
delta = engine.apply(operation);
sendToDeltaHub(JSON.stringify(delta));
var Delta = require('currency-market').Delta;
var delta = new Delta({
json: receivedJSON
});
state.apply(delta);
sendToFrontEnds(JSON.stringify(delta));
var Delta = require('currency-market').Delta;
var delta = new Delta({
json: receivedJSON
});
state.apply(delta);
var funds = state.getAccount('Peter').getBalance('EUR').funds
All functions complete synchronously and throw errors if they fail.
Amount
Amount
handles large numerical arithmetic accurately (unlike the built in Javascript number implementation). It is used for all amount and price values and is provided as a utility for applications to apply the same arimthmetic functionality in their own contexts.
Amount
instances are immutable.
Divisions are carried out to an arbitrary precision of 25 decimal points.
var Amount = require('currency-market').Amount;
// Always initialise from a string representation of a number
var amount1000 = new Amount('1000');
var amount200 = new Amount('200');
// multiply 2 amounts
var amount200000 = amount1000.multiply(amount200);
// add 2 amounts
var amount1200 = amount1000.add(amount200);
// subtract 2 amounts
var amount800 = amount1000.subtract(amount200);
// divide 2 amounts
var amountPoint2 = amount200.divide(amount1000);
// Return the string representation of an amount
var str1000 = amount1000.toString();
// Compare 2 values
amount1000.compareTo(amount200) > 0;
amount200.compareTo(amount1000) < 0;
amount200.compareTo(amount200) == 0;
// 2 Identity constants are defined
var zero = Amount.ZERO;
var one = Amount.ONE;
Engine
var Engine = require('currency-market').Engine;
Engine
instances accept operations and return deltas that can be applied to simplified State
instances.
// Define a commission rate of 0.5%
var COMMISSION_RATE = new Amount('0.005');
// instantiate an engine
var engine = new engine({
// Optionally specify how commission should be applied to credits resulting from trades.
// If this is not specified then no commission will be charged
commission: {
// The ID of the account to receive the commission
account: 'commission',
// The callback to use for calculating the commission amount to subtract from a credit
// resulting from a trade
calculate: function(params) {
// A timestamp for the trade being executed
var timestamp = params.timestamp;
// The ID of the account that is being credited
var account = params.account;
// The currency of the credited amount
var currency = params.currency;
// The amount that is being credited as an Amount instance
var amount = params.amount;
// Return an object containing the amount of commission to deduct as an Amount
// instance and a reference for the commission rate/type being charged
//
// Note that it's best to avoid divisions when calculating commissions so as
// to avoid rounding errors. Also, as the reference is intended be transmitted along
// with market deltas, it should be possible to convert it losslessly to and from JSON
return {
amount: amount.multiply(COMMISSION_RATE),
reference: COMMISSION_RATE + '%'
};
}
}
});
apply
methodThe apply
method applies operations and returns the resulting deltas.
Operations and deltas can be converted losslessly to and from JSON for transmission.
If an operation fails for any reason (eg. not enough funds) then an error will be thrown.
Only operations that have been accepted using the Operation.accept
method can be applied to an Engine
instance.
try {
var delta = engine.apply(new Operation({
// Operation parameters
...
}));
} catch(error) {
// Possible errors will include invalid parameters or insufficient funds to complete the operation
...
}
JSON.stringify
Engines can be converted to and from JSON
var json = JSON.stringify(engine);
var engine = new Engine({
commission: {
account: 'commission',
calculate: function(params) {
return {
amount: amount.multiply(COMMISSION_RATE),
reference: COMMISSION_RATE + '%'
};
}
},
json: json
});
// OR
var json = JSON.stringify(engine);
var engine = new Engine({
commission: {
account: 'commission',
calculate: function(params) {
return {
amount: amount.multiply(COMMISSION_RATE),
reference: COMMISSION_RATE + '%'
};
}
},
exported: JSON.parse(json)
});
Delta
var Delta = require('currency-market').Delta;
Delta
instances are returned by Engine
instances after successfully applying operations. They can be converted to JSON, transmitted, reconstructed from JSON and applied to State
instances.
All deltas have the following properties
// The delta sequence number. These will be generated consecutively by the engine
// for successful operations. As such they will not be synchronized with operation sequence
// numbers due to the possibility of operations throwing errors
var sequence = delta.sequence;
// The operation instance as supplied to the `apply` method
var operation = delta.operation;
// Additional state change information in a format specific to the operation type
var result = delta.result;
JSON.stringify
Deltas can be converted to and from JSON
var json = JSON.stringify(delta);
var delta = new Delta({
json: json
});
// OR
var json = JSON.stringify(delta);
var delta = new Delta({
exported: JSON.parse(json)
});
Operation
var Operation = require('currency-market').Operation;
Operation
instances are submitted to Engine
instances to apply operations. They can be converted to JSON, transmitted and reconstructed from JSON
All operations follow this pattern
var operation = new Operation({
// Application specified reference that is returned untouched with the operation details included in the delta.
// Care should be taken to ensure that this too can be converted to and from JSON
reference: '550e8400-e29b-41d4-a716-446655440000',
// The ID of the account submitting the operation
account: 'Peter',
// The operation details, the name of this property will determine the type of the operation
// and what additional fields need to be supplied
operationType: {
// Operation parameters
...
}
});
accept
methodOperations must be accepted before they can be applied to an Engine
instance to ensure they are associated with a sequence number and a timestamp
operation.accept({
// The operation sequence number. These must be consecutive for consecutive operations
sequence: 123456,
// The timestamp for the operation as a Unix time since epoch in milliseconds
timestamp: 1371737390976
});
deposit
operationDeposit funds into an account
var operation = new Operation({
reference: '550e8400-e29b-41d4-a716-446655440000',
account: 'Peter',
// deposit 1000 Euros to account ID 'Peter'
deposit: {
currency: 'EUR',
amount: new Amount('1000')
}
});
// On successful application the `delta.result` will have the following fields
// The new level of funds in the deposited currency as an `Amount` instance
var funds = delta.result.funds
withdraw
operationWithdraw funds from an account
var operation = new Operation({
reference: '550e8400-e29b-41d4-a716-446655440000',
account: 'Peter',
// withdraw 1000 Euros from account ID 'Peter'
withdraw: {
currency: 'EUR',
amount: new Amount('1000')
}
});
// On successful application the `delta.result` will have the following fields
// The new level of funds in the deposited currency as an `Amount` instance
var funds = delta.result.funds
submit
operationSubmit orders to the market. Both bid and offer orders can be submitted and follow this pattern
var operation = new Operation({
reference: '550e8400-e29b-41d4-a716-446655440000',
account: 'Peter',
// Place a bid order for 10 BTC offering 100 Euros per BTC
submit: {
// order parameters
...
}
});
// On successful application the `delta.result` will have the following fields
// The new level of locked funds in the order's offer currency
var lockedFunds = delta.result.lockedFunds
// Note that only one of `nextHigherOrderSequence` or `trades` will be set
// If the order is not at the top of the order book then the sequence number
// of the next order above it is returned. This is a hint to optimize the
// insertion of the order into a `State` instance
var nextHigherOrderSequence = delta.result.nextHigherOrderSequence;
// If the order was inserted at the top of the order book then an array of trades
// will be returned. This array will still be set, but will be empty, if no actual
// trades were made
//
// Note that the price at which any trade was executed will be given by the bid or
// offer price associated with the `right` order and that the volume traded in each
// currency is most easily referenced by the debit amounts associated with the `left`
// and `right` accounts in their respective order's offer currencies
var trades = delta.result.trades;
// `left` gives the changes to be applied to the order that was submitted and the
// account that submitted it
var left = trades[0].left;
// Only one of `left` or `right` will have a remainder and this
// signals the amount of the order that has not yet been executed.
// When no remainder is specified it signals that the order was
// completely executed. It is possible that neither `left` nor `right`
// will have a remainder if they completely satisfy each other
var remainder = left.remainder;
// The remaining bidAmount on the order
var bidAmount = remainder.bidAmount;
// The remaining offerAmount on the order
var offerAmount = remainder.offerAmount;
// The transaction fields signal by how much the account balances have changed
// and how much commission was applied
var transaction = left.transaction;
// The changes applied to the balance being debited
var debit = transaction.debit;
// The amount of the order's offer currency debited from the account
var amount = debit.amount;
// The new level of funds in the debited currency
var funds = debit.funds;
// The new level of locked funds in the debited currency
var lockedFunds = debit.lockedFunds;
// The changes applied to the balances being credited
var credit = transaction.credit;
// The amount of the order's bid currency credited to the account
var amount = credit.amount;
// The new level of funds in the credited currency
var funds = credit.funds;
// If the engine was instantiated with commission then the commission
// field will be set
var commission = credit.commission;
// The amount of the order's bid currency credited to the commission account
var amount = commission.amount;
// The new level of funds in the order's bid currency in the commission account
var funds = commission.funds;
// The reference associated with the commission calculation
var reference = commission.reference;
// `right` gives the changes to be applied to the order that was matched and the
// account that submitted it. This order will always be the order that is currently
// at the top of the opposing order book to that which the submitted order was added.
// The fields that can be set are the same as for `left`
var right = trades[0].right;
var operation = new Operation({
reference: '550e8400-e29b-41d4-a716-446655440000',
account: 'Peter',
// Place a bid for 10 BTC offering 100 Euros per BTC
submit: {
bidCurrency: 'BTC',
offerCurrency: 'EUR',
bidPrice: new Amount('100'),
bidAmount: new Amount('10')
}
});
var operation = new Operation({
reference: '550e8400-e29b-41d4-a716-446655440000',
account: 'Peter',
// Place an offer of 1000 EUR bidding 0.01 BTC per EUR
submit: {
bidCurrency: 'BTC',
offerCurrency: 'EUR',
offerPrice: new Amount('0.01'),
offerAmount: new Amount('1000')
}
});
cancel
operationOrders can be cancelled using the cancel operation.
var operation = new Operation({
reference: '550e8400-e29b-41d4-a716-446655440000',
account: 'Peter',
// Cancel the order submitted by acount ID 'Peter' with operation sequence 615368
cancel: {
sequence: 615368
}
});
// On successful application the `delta.result` will have the following fields
// The new level of locked funds in the order's offer currency
var lockedFunds = delta.result.lockedFunds;
JSON.stringify
Operations can be converted to and from JSON
var json = JSON.stringify(operation);
var operation = new Operation({
json: json
});
// OR
var json = JSON.stringify(operation);
var operation = new Operation({
exported: JSON.parse(json)
});
State
var State = require('currency-market').State;
State
instances provide simplified access to a market state. They do not contain the logic for matching orders but do accept Engine
generated deltas to keep them synchronized with Engine
instances
// instantiate a state
var state = new State({
commission:
// Note that the commission acount name should match the commission
// acount name from the engine to which this state will be synchronised
account: 'commission'
});
// instantiate a state from JSON stringified engine
var state = new State({
commission:
account: 'commission'
json: JSON.stringify(engine)
});
apply
methodThe apply
method is used to apply deltas generated by Engine
instances. The delta will be applied synchronously and errors may be thrown
try {
state.apply(delta);
} catch(error) {
// possible errors include out of sequence deltas
...
}
JSON.stringify
States can be converted to and from JSON
var json = JSON.stringify(state);
var state = new State({
json: json
});
// OR
var json = JSON.stringify(state);
var state = new State({
exported: JSON.parse(json)
});
getBook
methodThe getBook
method gives access to the order books keyed by bid and offer currency. Each book is an Array
of orders sorted as they will be matched for execution
var book = state.getBook({
bidCurrency: 'BTC',
offerCurrency: 'EUR'
});
// Get the top of the order book
var order = book[0];
// All orders have the following fields
//
// The sequence number
var sequence = order.sequence;
// The timestamp in milliseconds since epoch
var timestamp = order.timestamp;
// The account ID associated with the order
var account = order.account;
// The offer currency
var offerCurrency = order.offerCurrency;
// The bid currency
var bidCurrency = order.bidCurrency;
// Bid orders have the following additional fields
// The bid price as an `Amount` instance
var bidPrice = order.bidPrice;
// The bid amount as an `Amount` instance
var bidAmount = order.bidAmount;
// Offer orders have the following additional fields
// The offer price as an `Amount` instance
var offerPrice = order.offerPrice;
// The offer amount as an `Amount` instance
var offerAmount = order.offerAmount;
getAccount
methodThe getAccount
method gives access to the accounts as instances of Account
by account ID
var account = state.getAccount('Peter');
Account
The Account
class gives access to the properties of an account
orders
propertyThis is a collection of active orders keyed by sequence number (NB. it is an Object
and not an Array
)
var order = account.orders[5];
The orders are the same instances as those in the books retrieved with the getBook
method
getBalance
methodThe getBalance
method gives access to the balances of funds as instances of Balance
associated with an account, keyed by currency
var balance = account.getBalance('EUR');
Balance
The Balance
class gives access to the levels of funds and locked funds (when orders are outstanding) as Amount
instances
var funds = balance.funds;
var lockedFunds = balance.lockedFunds;
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality.
Run tests with
$ npm test
Run performance tests with
$ npm run-script perf
Copyright © 2013 Peter Halliday
Licensed under the MIT license.
FAQs
A synchronous implementation of a limit order based currency market
We found that currency-market demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.