cxgame is a fake cryptocurrency exchange that has a few goals in mind:
- Educational: Learn or test out new programmatic trading strategies.
- A game: Battle other bots to test your skills.
cxgame has 4 primary components:
cxgame.server.CxExchange
-- The exchange servercxgame.server.CxFeed
-- The broadcast channel. Exchange activity is broadcasted to all connected clients here (e.g. orders, server messages)cxgame.client.CxClient
-- The API for creating your own client to communicate with the CxExchange server.cxgame.server.CxServer
-- Wraps and controlsCxExchange
andCxFeed
.
cxgame can run in various modes (time limit, endless, whitelisted users, etc).
See the documentation below for server and client examples. Also take a look
at the examples/
directory for more information.
If you find missing features or bugs, please open an issue or a pull request.
No PyPI package as of yet. Install using:
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python setup.py install
After install, the command cxserve
will be available. Running it starts the
exchange and feed servers with default options (see: cxgame.server.main()
).
The primary websocket servers that run the exchange and broadcast feed:
- Registration and authentication. Stores user credentials.
- Stores user wallets (USD and crypto).
- Sets the (mid) market price.
- Handles limit orders (buy, sell, cancel).
- Handles market orders (buy & sell at market price).
- Stores past completed order detail.
- Stores past filled orders (order matches).
- Sends information on the feed channel to broadcast messages to clients.
CxServer()
init options:
-
exchange_port -- Websocket
CxExchange
listen port. default: 9877 (int) -
feed_port -- Websocket
CxFeed
listen port. default: 9876 (int) -
bind -- Address(es) to listen on. default: 0.0.0.0 (str)
-
usd_start -- New users will start with this much USD in their wallet. default: 10000.00 (Decimal, int, str, or float. Auto-converts to Decimal.)
-
crypto_start -- New users will start with this much crypto in their wallet. default: 10.0 (Decimal, int, str, or float. Auto-converts to Decimal.)
-
user_limit -- The max amount of users allowed to connect. default: None (int)
-
time_limit -- Exchange will run for this amount of time (in seconds). default: None (int)
-
whitelist -- A whitelist of users allowed to connect. All users must be connected before the exchange will run. default: None (list[str])
-
pem_file -- Path to SSL key/cert file. default: None (str)
-
ssl_verify -- Specify if SSL connections/certs are verified (set to False for self signed). default: True (bool)
-
admin_secret -- The admin password. Required to run certain commands like shutdown. default: admin (str)
-
is_started -- Tells the server to accept commands or not. If False, commands are rejected until the 'start' command is issued. default: True (bool)
NOTE: CxServer
is a wrapper that passes down options to CxExchange
and
CxFeed
.
The command cxserve
can be used to launch the exchange & feed server threads
and comes with command line option parsing.
To manually run a server, it can be done a few ways. Look at
examples/server.py
for a more in-depth example.
The following is a list and example of the exchange server's commands. These
are also implemented on the client side CxClient
, as shown in the examples.
NOTE: The response is a Response()
class from cxgame.client.Response
. It
contains these variables:
- status -- True or False
- msg -- Free text response. Helpful for debugging errors.
- data -- The data line. It has a varying type in the response and depends on the type method that was called.
- raw -- The raw json string returned from the server. For debugging purposes.
The CxClient.r()
helper method is a shortcut that wraps websockets.connect
and is short for run
.
It does create a new connection on every call, so not ideal for long running
clients and defeats the purpose of a websocket, but useful for testing or
demonstration purposes. See the cxgame/client.py
source code r()
and
runcmd()
methods for more explanation.
Register a new user on the exchange. The response will contain a token for future authentication.
from cxgame.client import CxClient
cx = CxClient(user='new-username123', uri='ws://mtingers.com:9877')
response = cx.r('register')
print(response.status, response.msg)
# If successful, store the token for later auth
if response.status:
token = response.data
cx = CxClient(user='root', uri='ws://mtingers.com:9877', token='123...')
response = cx.r('auth')
Broadcasts a message on the feed server:
response = cx.r('broadcast', 'Hello, World!')
Places a maker limit buy order:
price = '999.99'
size = '1.5'
response = cx.r('buy', price, size)
print('order:', response.data)
Places a market buy order. This attempts to match limit sell orders that are the closest in price at the time of the market order:
amount = '999.99' # The amount of your wallet's USD to use for this buy.
response = cx.r('buy_market', amount)
print('order:', response.data)
Places a maker limit sell order:
price = '1000.01'
size = '1.5'
response = cx.r('sell', price, size)
Places a market sell order. This attempts to match limit buy orders that are the closest in price at the time of the market order:
amount = '2.25' # The amount of your wallet's crypto to use for this sell.
response = cx.r('sell_market', amount)
mid_market_price = cx.r('price').data
Get your open orders:
my_orders = cx.r('orders').data
for order in my_orders:
print('id={} price={}'.format(order['id'], order['price']))
Get all open orders (all users):
all_orders = cx.r('all_orders').data
for order in all_orders:
print('user={} id={} price={}'.format(
order['user'], order['id'], order['price'])
)
Get your current wallets for USD and crypto:
wallets = cx.r('wallets').data
print('crypto={} usd={}'.format(wallets['crypto'], wallets['usd']))
Cancel an order, by order ID:
cancelled = cx.r('cancel', order_id)
Get your fills. This is orders that were matched partially or full:
my_fills = cx.r('fills').data
for fill in my_fills:
print(fill)
Get your completed orders. These are orders that were completed/filled.
my_completed_orders = cx.r('completed').data
for order in my_completed_orders:
print(order)
For debugging and admin use. Runs an audit on all order status to detect bugs in the code (invalid states).
cx = CxClient(user='root', uri='ws://mtingers.com:9877', token='123...')
response = cx.r('auth')
response = cx.r('shutdown', 'adminpassword123')
cx = CxClient(user='root', uri='ws://mtingers.com:9877', token='123...')
response = cx.r('auth')
response = cx.r('start', 'adminpassword123')
cx = CxClient(user='root', uri='ws://mtingers.com:9877', token='123...')
response = cx.r('auth')
response = cx.r('pause', 'adminpassword123')
An open test exchange is running at: ws://mtingers.com:9877
The feed for this server is at: ws://mtingers.com:9876
- Add admin user auth
- Finish mypy typing
- Finish docstrings
- Add logging
- Add state saving
- Add tests
- Add PyPI
- Add server CLI arg parsing