4 columns for ticker tape
This commit is contained in:
parent
c1447e5852
commit
281c3b1fe1
1 changed files with 71 additions and 24 deletions
|
|
@ -4,9 +4,9 @@
|
||||||
Reads a CSV of trades (timestamp, symbol, lots, price) and generates
|
Reads a CSV of trades (timestamp, symbol, lots, price) and generates
|
||||||
ESC/POS commands with 90° rotated text to emulate an old stock ticker tape.
|
ESC/POS commands with 90° rotated text to emulate an old stock ticker tape.
|
||||||
|
|
||||||
Each character gets its own printed row. When the receipt paper is turned
|
Trades are distributed across 4 parallel columns. Each column independently
|
||||||
sideways, symbol characters read across the top and price characters read
|
prints symbol characters at the top of the tape and price/lot characters at
|
||||||
across the bottom — just like a real ticker tape.
|
the bottom — just like a real multi-track ticker tape.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
|
|
@ -19,32 +19,85 @@ from glyphs import price_to_fraction, upload_fractions
|
||||||
# TM-T88V with 90° rotation: Font A chars are 24 dots wide (instead of 12),
|
# TM-T88V with 90° rotation: Font A chars are 24 dots wide (instead of 12),
|
||||||
# so 512 printable dots / 24 = 21 chars fit across the 80mm paper width.
|
# so 512 printable dots / 24 = 21 chars fit across the 80mm paper width.
|
||||||
LINE_WIDTH = 21
|
LINE_WIDTH = 21
|
||||||
|
NUM_COLUMNS = 4
|
||||||
|
COL_WIDTH = LINE_WIDTH // NUM_COLUMNS # 5
|
||||||
|
|
||||||
|
|
||||||
def print_trade(p, symbol, lots, price):
|
def format_price(lots, price):
|
||||||
"""Print a single trade as ticker tape characters, one per row."""
|
"""Format price/lots string for a trade."""
|
||||||
frac = price_to_fraction(price)
|
frac = price_to_fraction(price)
|
||||||
if lots > 1:
|
if lots > 1:
|
||||||
price_str = f"{lots}s{frac}"
|
return f"{lots}s{frac}"
|
||||||
else:
|
return frac
|
||||||
price_str = frac
|
|
||||||
|
|
||||||
# Symbol characters at the top (right-aligned)
|
|
||||||
for ch in symbol:
|
|
||||||
p.text(" " * (LINE_WIDTH - 1) + ch + "\n")
|
|
||||||
|
|
||||||
# Price characters at the bottom (left-aligned)
|
def trade_char_count(symbol, price_str):
|
||||||
for ch in price_str:
|
"""Total characters a trade occupies in a column (symbol + price + separator)."""
|
||||||
p.text(ch + "\n")
|
return len(symbol) + len(price_str) + 1
|
||||||
|
|
||||||
# Blank line separator between trades
|
|
||||||
p.text("\n")
|
def assign_to_columns(trades):
|
||||||
|
"""Assign trades to columns, always picking the column with the fewest chars."""
|
||||||
|
columns = [[] for _ in range(NUM_COLUMNS)]
|
||||||
|
lengths = [0] * NUM_COLUMNS
|
||||||
|
|
||||||
|
for symbol, lots, price in trades:
|
||||||
|
price_str = format_price(lots, price)
|
||||||
|
count = trade_char_count(symbol, price_str)
|
||||||
|
shortest = lengths.index(min(lengths))
|
||||||
|
columns[shortest].append((symbol, price_str))
|
||||||
|
lengths[shortest] += count
|
||||||
|
|
||||||
|
return columns
|
||||||
|
|
||||||
|
|
||||||
|
def build_column_chars(trades_in_column):
|
||||||
|
"""Build the character sequence for one column.
|
||||||
|
|
||||||
|
Each entry is ('top', ch), ('bottom', ch), or ('blank',) controlling
|
||||||
|
where the character is placed within the column width.
|
||||||
|
"""
|
||||||
|
chars = []
|
||||||
|
for symbol, price_str in trades_in_column:
|
||||||
|
for ch in symbol:
|
||||||
|
chars.append(("top", ch))
|
||||||
|
for ch in price_str:
|
||||||
|
chars.append(("bottom", ch))
|
||||||
|
chars.append(("blank",))
|
||||||
|
return chars
|
||||||
|
|
||||||
|
|
||||||
|
def print_all(p, trades):
|
||||||
|
"""Print all trades across multiple columns, one row at a time."""
|
||||||
|
columns = assign_to_columns(trades)
|
||||||
|
col_chars = [build_column_chars(col) for col in columns]
|
||||||
|
max_len = max(len(cc) for cc in col_chars)
|
||||||
|
|
||||||
|
for row_idx in range(max_len):
|
||||||
|
row = [" "] * LINE_WIDTH
|
||||||
|
for col_idx in range(NUM_COLUMNS):
|
||||||
|
col_start = col_idx * COL_WIDTH
|
||||||
|
if row_idx < len(col_chars[col_idx]):
|
||||||
|
entry = col_chars[col_idx][row_idx]
|
||||||
|
if entry[0] == "top":
|
||||||
|
row[col_start + COL_WIDTH - 1] = entry[1]
|
||||||
|
elif entry[0] == "bottom":
|
||||||
|
row[col_start] = entry[1]
|
||||||
|
p.text("".join(row) + "\n")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
csv_file = sys.argv[1] if len(sys.argv) > 1 else "trades_sample_sorted.csv"
|
csv_file = sys.argv[1] if len(sys.argv) > 1 else "trades_sample_sorted.csv"
|
||||||
output_file = sys.argv[2] if len(sys.argv) > 2 else "ticker_tape.bin"
|
output_file = sys.argv[2] if len(sys.argv) > 2 else "ticker_tape.bin"
|
||||||
|
|
||||||
|
# Read all trades
|
||||||
|
trades = []
|
||||||
|
with open(csv_file) as f:
|
||||||
|
reader = csv.reader(f)
|
||||||
|
for row in reader:
|
||||||
|
_timestamp, symbol, lots_str, price_str = row
|
||||||
|
trades.append((symbol, int(lots_str), float(price_str)))
|
||||||
|
|
||||||
p = Dummy(profile="TM-T88V")
|
p = Dummy(profile="TM-T88V")
|
||||||
p.hw("INIT")
|
p.hw("INIT")
|
||||||
# ESC V 1: 90° clockwise character rotation (not exposed by python-escpos)
|
# ESC V 1: 90° clockwise character rotation (not exposed by python-escpos)
|
||||||
|
|
@ -55,15 +108,9 @@ def main():
|
||||||
# Upload custom fraction glyphs (⅛ ¼ ⅜ ½ ⅝ ¾ ⅞)
|
# Upload custom fraction glyphs (⅛ ¼ ⅜ ½ ⅝ ¾ ⅞)
|
||||||
upload_fractions(p)
|
upload_fractions(p)
|
||||||
|
|
||||||
with open(csv_file) as f:
|
print_all(p, trades)
|
||||||
reader = csv.reader(f)
|
|
||||||
for row in reader:
|
|
||||||
_timestamp, symbol, lots_str, price_str = row
|
|
||||||
lots = int(lots_str)
|
|
||||||
price = float(price_str)
|
|
||||||
print_trade(p, symbol, lots, price)
|
|
||||||
|
|
||||||
# Feed past the cutter (~20mm above print head, ~12 lines at 12-dot spacing)
|
# Feed past the cutter (~20mm above print head)
|
||||||
p.text("\n" * 16)
|
p.text("\n" * 16)
|
||||||
p.cut()
|
p.cut()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue